Compilation of ghost functions in SPARK 2014 - ada

I wish to abstract out some common patterns in pre and post conditions into a function in order to make the conditions more readable. From the documentation, it looks like ghost functions solve this issue.
I depend on the fact that overflows are eliminated in assertions (via a configuration pragma) so I don't believe that I can use ghost functions with code. The SPARK documentation contains the following example of a ghost function without code in http://docs.adacore.com/spark2014-docs/html/ug/spark_2014.html#ghost-functions :
function A_Nonexecutable_Ghost_Function (Lo, Hi : Natural) return Natural
with Pre => Lo <= Hi,
Post => A_Nonexecutable_Ghost_Function'Result in Lo .. Hi,
Convention => Ghost,
Import;
-- The body of the function is not declared elsewhere.
However this, and any other ghost function like it that I try to write, results in:
warning: null global effect assumed on imported subprogram
And that warning stops compilation. Trying to say Global => () doesn't work. Can I suppress the warning? There's no obvious -Werror on the gnatprove command line that I can see to remove either.

The proper syntax is Global => null instead of Global => (), then the warning goes away.

Related

Ada: Incompatible alignment warning issued when specifying an address clause

Is there an ideal way to avoid compile-time warnings about incompatible alignment raised when using the import clause on architectures with strict alignment requirements?
This behavior is described in the GCC manual here. I'm compiling with riscv64-elf-gnat. All warnings are enabled, and warnings treated as errors. I understand that I can suppress this particular warning. Is there an ideal way to handle this scenario in code?
This warning seems rather arbitrary. It doesn't seem to be possible to handle this scenario within the exception handling block of the function itself.
Any help here would be appreciated.
For example, the code below:
----------------------------------------------------------------------------
-- Read_Unsigned_8
----------------------------------------------------------------------------
function Read_Unsigned_8 (
Addr : System.Address
) return Unsigned_8 is
Data : Unsigned_8
with Import,
Address => Addr;
begin
return Data;
end Read_Unsigned_8;
Raises this warning:
mmio.adb:23:09: warning: pragma Restrictions (No_Exception_Propagation) in effect
mmio.adb:23:09: warning: "Program_Error" may call Last_Chance_Handler
mmio.adb:23:09: warning: address value may be incompatible with alignment of object
With arm-eabi, you can make this over-enthusiastic (not to say wrong) warning go away by specifying the alignment:
Data : Unsigned_8
with
Import,
Address => Addr,
Alignment => 1;

Deep copy of function arguments for polymorphic types

I use an object based on a base package roughly defined as:
package Base is
type T_Base is abstract tagged null record;
-- This performs a deep copy. Shallow copies may lead to STORAGE_ERROR.
-- This shall be implemented by every derived type.
function Copy (From : in T_Base) return T_Base'Class is abstract;
end package Base;
This package is derived by several packages which are further derived
package Foo is
type T_Foo is new T_Base with record
A_Data : Natural; -- Of course, in the real code, these are types by far more complex.
end record;
procedure do_something (Foo_Object : in T_Foo);
-- This implements the deep copy
function Copy (From : in T_Foo) return T_Base'Class is abstract;
end package Foo;
On calling the procedure do_something, I do get a storage_error:
procedure handle_received_foo (Foo_In: in Foo.T_Foo) is
begin
Foo.do_something (Foo_Object => Foo_In); -- The storage error does happen here.
end main;
When running the code with gdb, I get a segfault on entering the function and I get:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 39 (LWP 39)]
0x033c9828 in foo.do_something (foo_object=...) at ./foo.adb:67
67 procedure do_something (Foo_Object : in T_Foo);
(gdb) p foo_object
$1 (null)
So I guess I get a storage_error when doing the shallow copy of the argument Foo_Object.
I am aware that this is no MWE and that there might be a mistake in one of the types present used in the derived types.
I can't find any good option:
Making T_Foo a Controlled type to call Copy in Adjust seems not to be possible without greatly changing its definition as I can't derive T_Foo both from T_Base and Ada.Finalization.Controlled since none of them is an interface types
Defining T_Base as
type T_Base is abstract new Ada.Finalization.Controlled with null record;
and override Adjust here seems to induce a hell lot too much modifications on the existing code base as gnat yields in multiple places
type of aggregate has private ancestor "Controlled" must use extension aggregate.
So I'm low on solutions to either investigate the problem further or to solve it with a hammer.
The problem was not in the Copy function. The comments I saw in the code base were misleading.
The fact that introducing new variables changed the location of the exception made me consider some stack overflow problems.
Indeed the Storage_Size allocated for the task was not sufficient. Increasing the pragma Storage_Size(<value>) solved the problem.
Since the code base is compiled with -fstack-check, this led to the aforementioned STORAGE_ERROR.
More infos on Adacore's documentation.
This could probably have been seen with Gem #95: Dynamic Stack Analysis in GNAT but I am not currently able to see any result with the binding option suggested therein.

How and why is a GNAT.Strings.String_List use clause disallowed? How can you use System.Strings.String_List."&" with infix notation?

Posting for two reasons: (1) I was stuck on unhelpful compiler errors for far too long for such a simple issue and I want the next person to google those messages to come upon my (or other) answers, and (2) I still don't understand disallowing a use clause, so my own answer is really incomplete.
In order to call a program in two places with mostly the same arguments, I want to use the '&' to append to a default list inline:
declare
Exit_Code : constant Integer := GNAT.OS_Lib.Spawn (Program_Name => "gprbuild", Args => (Default_GPR_Arguments & new String'(File_Name_Parameter)));
begin
if Exit_Code /= 0 then
raise Program_Error with "Exit code:" & Exit_Code'Image;
end if;
end;
However, the compiler complains that System.Strings.String_List needs a use clause:
operator for type "System.Strings.String_List" is not directly visible
use clause would make operation legal
But inserting use System.Strings.String_List yields:
"System.Strings.String_List" is not allowed in a use clause
I also got this warning:
warning: "System.Strings" is an internal GNAT unit
warning: use "GNAT.Strings" instead
So I substituted GNAT for System in the with and the use clause and got an extra error in addition to the original 'you need a use clause for System.Strings.String_List' one:
"GNAT.Strings.String_List" is not allowed in a use clause
Why is GNAT.Strings.String_List not allowed in a use clause? Section 8.5 on use clauses doesn't seem to state anything on disallowed packages, so is this a compiler bug? Is it possible to define a new package that cannot have a use clause?
In a use clause of the form
use Name;
Name must be a package name. GNAT.Strings.String_List is a subtype name, not a package name.
There are a number of ways to invoke "&" for String_List. The simplest is to use the full name:
GNAT.Strings."&" (Left, Right)
but presumably you want to be able to use it as an operator in infix notation, Left & Right. Ways to achieve this, in decreasing specificity:
function "&" (Left : GNAT.Strings.String_List; Right : GNAT.Strings.String_List) return GNAT.Strings.String_List renames GNAT.Strings."&"; This makes this specific function directly visible.
use type GNAT.Strings.String_List; This makes all primitive operators of the type directly visible.
use all type GNAT.Strings.String_List; This makes all primitive operations of the type (including non-operator operations) directly visible.
use GNAT.Strings; This makes everything in the package directly visible.
Looks like it is a design decision. And many other packages in System follows this rule. From the s-string.ads (package specification for System.String):
-- Note: this package is in the System hierarchy so that it can be directly
-- be used by other predefined packages. User access to this package is via
-- a renaming of this package in GNAT.String (file g-string.ads).
My guess why this is done in that way: because it isn't in the Ada specification, but extension from GNAT.

How to systematically populate a whitelist for a sandboxing program?

On pp. 260-263 of Programming in Lua (4th ed.), the author discusses how to implement "sandboxing" (i.e. the running of untrusted code) in Lua.
When it comes to imposing limiting the functions that untrusted code can run, he recommends a "whitelist approach":
We should never think in terms of what functions to remove, but what functions to add.
This question is about tools and techniques for putting this suggestion into practice. (I expect there will be confusion on this point I want to emphasize it upfront.)
The author gives the following code as an illustration of a sandbox program based on a whitelist of allowed functions. (I have added or moved around some comments, and removed some blank lines, but I've copied the executable content verbatim from the book).
-- From p. 263 of *Programming in Lua* (4th ed.)
-- Listing 25.6. Using hooks to bar calls to unauthorized functions
local debug = require "debug"
local steplimit = 1000 -- maximum "steps" that can be performed
local count = 0 -- counter for steps
local validfunc = { -- set of authorized functions
[string.upper] = true,
[string.lower] = true,
... -- other authorized functions
}
local function hook (event)
if event == "call" then
local info = debug.getinfo(2, "fn")
if not validfunc[info.func] then
error("calling bad function: " .. (info.name or "?"))
end
end
count = count + 1
if count > steplimit then
error("script uses too much CPU")
end
end
local f = assert(loadfile(arg[1], "t", {})) -- load chunk
debug.sethook(hook, "", 100) -- set hook
f() -- run chunk
Right off the bat I am puzzled by this code, since the hook tests for event type (if event == "call" then...), and yet, when the hook is set, only count events are requested (debug.sethook(hook, "", 100)). Therefore, the whole song-and-dance with validfunc is for naught.
Maybe it is a typo. So I tried experimenting with this code, but I found it very difficult to put the whitelist technique in practice. The example below is a very simplified illustration of the type of problems I ran into.
First, here is a slightly modified version of the author's code.
#!/usr/bin/env lua5.3
-- Filename: sandbox
-- ----------------------------------------------------------------------------
local debug = require "debug"
local steplimit = 1000 -- maximum "steps" that can be performed
local count = 0 -- counter for steps
local validfunc = { -- set of authorized functions
[string.upper] = true,
[string.lower] = true,
[io.stdout.write] = true,
-- ... -- other authorized functions
}
local function hook (event)
if event == "call" then
local info = debug.getinfo(2, "fnS")
if not validfunc[info.func] then
error(string.format("calling bad function (%s:%d): %s",
info.short_src, info.linedefined, (info.name or "?")))
end
end
count = count + 1
if count > steplimit then
error("script uses too much CPU")
end
end
local f = assert(loadfile(arg[1], "t", {})) -- load chunk
validfunc[f] = true
debug.sethook(hook, "c", 100) -- set hook
f() -- run chunk
The most significant differences in the second snippet relative to the first one are:
the call to debug.sethook has "c" as mask;
the f function for the loaded chunk gets added to the validfunc whitelist;
io.stdout.write is added to the validfunc whitelist;
When I use this sandbox program to run the one-line script shown below:
# Filename: helloworld.lua
io.stdout:write("Hello, World!\n")
...I get the following error:
% ./sandbox helloworld.lua
lua5.3: ./sandbox:20: calling bad function ([C]:-1): __index
stack traceback:
[C]: in function 'error'
./sandbox:20: in function <./sandbox:16>
[C]: in metamethod '__index'
helloworld.lua:3: in local 'f'
./sandbox:34: in main chunk
[C]: in ?
I tried to fix this by adding the following to validfunc:
[getmetatable(io.stdout).__index] = true,
...but I still get pretty much the same error. I could go on guessing and trying more things to add, but this is what I would like to avoid.
I have two related questions:
What can I add to validfunc so that sandbox will run helloworld (as is) to completion?
More importantly, what is a systematic way to find determine what to add to a whitelist table?
Part (2) is the heart of this post. I am looking for tools/techniques that remove the guesswork from the problem of populating a whitelist table.
(I know that I can get helloworld to work if I replace io.stdout:write with print, register print in sandbox's validfunc, and pass {print = print} as the last argument to loadfile, but doing this does not answer the general question of how to systematically determine what needs to be added to the whitelist to allow some specific code to work in the sandbox.)
EDIT: Ask #DarkWiiPlayer pointed out, the calling bad function error is being triggered by the calling of an unregistered function (__index?), which happened as part of the response to an earlier attempt to index a nil value error. So, this post's questions are all about systematically determining what to add to validfunc to allow Lua to emit the attempt to index a nil value error normally.
I should add that the question of which function's call triggered the hook's execution responsible for the calling bad function error message is at the moment completely unclear. This error message blames the error on __index, but I suspect that this may be a red herring, possibly due to a bug in Lua.
Why suspect a bug in Lua? If I change the error call in sandbox slightly to
error(string.format("calling bad function (%s:%d): %s (%s)",
info.short_src, info.linedefined, (info.name or "?"),
info.func))
...then the error message looks like this:
lua5.3: ./sandbox:20: calling bad function ([C]:-1): __index (function: 0x55b391b79ef0)
stack traceback:
[C]: in function 'error'
./sandbox:20: in function <./sandbox:16>
[C]: in metamethod '__index'
helloworld.lua:3: in local 'f'
./sandbox:34: in main chunk
[C]: in ?
Nothing surprising there, but if now I change helloworld.lua to
# Filename: helloworld.lua
nonexistent()
io.stdout:write("Hello, World!\n")
...and run it under sandbox, the error message becomes
lua5.3: ./sandbox:20: calling bad function ([C]:-1): nonexistent (function: 0x556a161cdef0)
stack traceback:
[C]: in function 'error'
./sandbox:20: in function <./sandbox:16>
[C]: in global 'nonexistent'
helloworld.lua:3: in local 'f'
./sandbox:34: in main chunk
[C]: in ?
From this error message, one may conclude that nonexistent is a real function; after all, it's sitting right there at 0x556a161cdef0! But we know that nonexistent lives up to its name: it doesn't exist!
The whiff of a bug is definitely in the air. It could be that the function that is triggering the hook should really be excluded from those that trigger such "c"-masked hooks? Be that as it may, it appears that, in this particular situation, the call to debug.info is returning inconsistent information (since the name of the function [e.g. nonexistent] clearly does not correspond at all to the actual function object [e.g. function: 0x556a161cdef0] that is supposedly triggering the hook).
(Final answer at the bottom, feel free to skip until the <hr> line)
I'll explain my debugging step by step.
This is a really weird phenomenon. After some testing, I've managed to narrow it down a bit:
Since you pass {} to load, the function runs with an empty environment, so io is, in fact, nil (and io.stdout would error anyway)
The error happens directly when attempting to index io (which is a nil value)
The functio __index is a C function (see error message)
My first intuition was that __index was called somewhere internally. Thus, to find out what it does, I decided to look at its locals in hopes of guessing what it does.
A quick helper function I threw together:
local function locals(f)
return function(f, n)
local name, value = debug.getlocal(f+1, n)
if name then
return n+1, name, value
end
end, f, 1
end
Insert that right before the line where the error is raised:
for idx, name, value in locals(2) do
print(name, value)
end
error(string.format("calling bad function (%s:%d): %s", info.short_src, info.linedefined, (info.name or "?")))
This led to an interesting result:
(*temporary) stdin:43: attempt to index a nil value (global 'io')
(*temporary) table: 0x563cef2fd170
lua: stdin:29: calling bad function ([C]:-1): __index
stack traceback:
[C]: in function 'error'
stdin:29: in function <stdin:21>
[C]: in metamethod '__index'
stdin:43: in function 'f'
stdin:49: in main chunk
[C]: in ?
shell returned 1
Why is there a temporary string value with a completely different error message?
By the way, this error makes total sense; io does not exist because of the empty environment, so indexing it should obviously raise just that error.
It's honestly a very interesting error, but I'll leave it at this, as you're learning the language and this hint might be enough for you to figure it out on your own. It's also a very nice chance to actually use (and get to know) the debug module in a more practical context.
Actual Solution
After some time has now passed, I came back to add a proper solution to this problem, but I really already did just that. The weird error reporting is just Lua being weird. The real error is the empty environment that's set when loading the chunk, as I mentioned a few paragraphs above.
From the manual:
load (chunk [, chunkname [, mode [, env]]])
Loads a chunk.
[...]
If the resulting function has upvalues, the first upvalue is set to the value of env, if that parameter is given, or to the value of the global environment. Other upvalues are initialized with nil. (When you load a main chunk, the resulting function will always have exactly one upvalue, the _ENV variable (see ยง2.2). However, when you load a binary chunk created from a function (see string.dump), the resulting function can have an arbitrary number of upvalues.) All upvalues are fresh, that is, they are not shared with any other function.
[...]
Now, in a "main chunk", i.e. one loaded from a text Lua file, the first (and only) upvalue is always the environment of the chunk, so where it will look for "globals" (this is slightly different in Lua 5.1). Since an empty table is passed in, the chunk has no access to any of the global variables like string or io.
Therefore, when the function f() tries to index io, Lua throws an error "attempt to index a nil value", because io is nil. For whatever reason Lua then makes some internal function calls that end up triggering the blacklist, causing a new error that shadows the previous one; this makes debugging this error extremely inconvenient and almost impossible without using the debug library to get additional information about the call stack.
I ultimately only realized this myself after I noticed the original error message while looking at the locals of the function that made the blocked call.
I hope this solves the problem :)

Memory allocation with storage manager leads to not pure function (RM 13.1(22))

Porting some old code to a newer CentOs Linux machine.
I am using linux gnat with a couple of flags:
Default_Switches ("ada") use ("-fstack-check", "-g", "-gnatVr", "-gnato", "-gnatE", "-gnatwmuv", "-gnata", "-m32");
and I have the gnat version:
gcc-gnat.i686 4.8.5-11.el7
So these are the preconditions.
I have now an for sure working self-written storage manager which is called by
St_Wa.Alloc(StoragePool, BitSize)
So now to my problem and to be honest I do not really get the point why the compiler is failing, so I would be really grateful for a detailed explanation why it is not working!
function AllocMem(StoragePool : in St_Wa.Mem_Pool_Type;
Option: in Option_Type)
return Option_Ref is
subtype New_Type is Option_Type (Option.Kind);
New_Option : New_Type;
for New_Option use at St_Wa.Alloc( StoragePool => StoragePool,
BitSize => New_Type'Size)
begin
Bl_Bl.Move( ... sth happens here ... )
return Pointer(New_Option'Address);
end AllocMem;
Whereas:
type Option_Type ( Kind : Option_Kind_Type := Marker) is
record
Next : Option_Ref;
case Kind is
when First_Procedure => First_Procedure : First_Procedure_Type;
when Sec_Procedure => Sec_Procedure : Sec_Procedure_Type;
end case;
end record;
And I get the following Error:
invalid address clause for initialized object "New_Option"
function "Alloc" is not pure (RM 13.1 (22))
Do I get this error because I have a switch case in the type with conditions and therefore the size is only determined depending on kind? How can I avoid this without rewriting everything?
Do I get this error because I have a switch case in the type with conditions and therefore the size is only determined depending on kind?
No. The paragraph referenced by the error message (RM 13.1 (22)) in the LRM reads:
An implementation need not support representation items containing nonstatic expressions, except that an implementation should support a representation item for a given entity if each nonstatic expression in the representation item is a name that statically denotes a constant declared before the entity.
Now, the representation item here is the call to Alloc since your code:
for New_Option use at St_Wa.Alloc(StoragePool => StoragePool,
BitSize => New_Type'Size);
is Ada83-style for
for New_Option'Address use St_Wa.Alloc(StoragePool => StoragePool,
BitSize => New_Type'Size);
And since Alloc (...) is a function call, it is not a static expression, since static functions are, according to RM 4.9:
a predefined operator whose parameter and result types are all scalar types none of which are descendants of formal scalar types;
a predefined concatenation operator whose result type is a string type;
an enumeration literal;
a language-defined attribute that is a function, if the prefix denotes a static scalar subtype, and if the parameter and result types are scalar.
Since Alloc is none of the above, as RM 13.1 states, the implementation does not need to support it in the representation item. However, the actual error message tells us that "Alloc" is not pure, so GNAT tells it it would support this if Alloc was pure.
So one way to fix this would be to make Alloc pure, which means: Add pragma Pure; to the package St_Wa, which contains Alloc. Whether this is possible depends on the package and it might require additional changes.
If this is not feasible, RM 13.1 (22) hints at another way: A nonstatic expression should be supported if it denotes a constant declared before the entity. Thus, this should work:
My_Address : constant System.Address :=
St_Wa.Alloc(StoragePool => StoragePool, BitSize => New_Type'Size);
New_Option : New_Type;
for New_Option use at My_Address;

Resources