Page 1 of 1

How to unpack NULL string in libffi

Posted: Mon May 18, 2015 6:28 pm
by kosh
`unpack` function with LIBFFI segfault when variable type "char*" is NULL.

Code: Select all

struct string {
    char *ptr;
    size_t len;
};

Code: Select all

newLISP v.10.6.2 32-bit on Win32 IPv4/6 UTF-8 libffi, options: newlisp -h

> (struct '_struct_string "char*" "long")
_struct_string
> (setf s (pack _struct_string 0 0))
"\000\000\000\000\000\000\000\000"
> (unpack _struct_string s)
Segment fault
I expected following behavior:

Code: Select all

> (unpack _struct_string s)
(nil 0)
How to avoid something this?

(It can use `unpack` without LIBFFI. but code will be more complex.)

Re: How to unpack NULL string in libffi

Posted: Mon May 18, 2015 8:11 pm
by Lutz
In version 10.6.3 this will cause and error message to be thrown:

Code: Select all

newLISP v.10.6.3 64-bit on OSX IPv4/6 UTF-8 libffi, options: newlisp -h

> (struct '_struct_string "char*" "long")
_struct_string
> (setf s (pack _struct_string 0 0))
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
> (unpack _struct_string s)

ERR: cannot convert NULL to string in function unpack
> 
... the same happens when a libffi imported function returns NULL or when calling get-string with 0. To avoid this you could either use:

Code: Select all

(catch (unpack _struct_string s) 'ret)
this will return nil and an error message in ret, or will return true and the returned list in ret.

Another possibility is using void* as a type instead and test if a 0 comes back, before doing a get-string on a valid pointer. get-string on a NULL ptr will throw the same error message.

Like you, Ted too suggested returning a nil for a NULL pointer. I am afraid people then start passing nil as parameter (instead of 0) when calling functions. The implications in newLISP code base are just too much when treating NULL as nil and vice<->versa. The current solution just inserts a NULL test in the internal string => lisp cell - stuffString() - C function and this way covers all occurrences in the system when trying to convert a 0 (NULL) to a a string, and there are many occurrences.

In most cases a NULL ptr will occur, this is an exception, not necessarily an error but a situation relatively rare.

Another solution would be, to not throw an error but return a "(null)". I think some GNU C-library functions to this. This could be implemented, instead of throwing the error as done currently in 10.6.3.

But I still prefer the error message and trapping it with catch, because in any case when ther is a chance for NULL then one would have test for "(null)" in the returned string of a function or unpack. As this is an execpetional case working with catch seems to be more natural.

Re: How to unpack NULL string in libffi

Posted: Tue May 19, 2015 1:46 am
by TedWalther
Lutz, I'm not suggesting to return nil for NULL pointer. I appreciate you want to keep C data types separate from the LISP ones. But when dealing with char*, you are treating it as a string; surely it isn't an error for a function to return either a string, or a nil for "no string"? I suggest this, because char* isn't returning a pointer, you disabled that in 10.4.3, it is returning a string. I'm comfortable with a function returning either a valid string or a nil. Otherwise, the void* like you suggest is the right way. I asked for "nil" not to match it up with NULL, but because the char* implies more integration with LISP, doing things in a LISPy way, so I can forget about the C a bit.

Returning an error in this case is very surprising; when we use char* and we usually get a string, then for NULL we would expect "no string". Not the empty string, but just a "false" value.

Also if people pass "nil" to a C function that has char*, the NULL translation makes sense, because it means "no string, no pointer, nothing". It maintains the integration between newLisp and C. If they pass nil when it is void*, that should be an error. I only say this, because you made it clear in the documentation that char* doesn't map to actual address, it is glue that translates between strings and C types.

So, I saw char* as a convenience thing, when we only need void*. And it is a nice convenience, I love how easy it is to make a C function integrate with newLisp like they are soul-mates.

I would suggest also that if a "char*" is one of the functions arguments, that ONLY a string or nil would be allowed, and not a number/address. To maintain the distinction that char* is an integration convenience.