Memory fault error

Elliott Johnson elliott at elliottjohnson.net
Fri Apr 27 23:48:57 UTC 2018


Martin,

Thanks for the reply.

On 4/27/18 3:42 AM, Martin Simmons wrote:
> I think that answer is "you are not supposed to do that" because there is no
> documentation for the slots of sensors_chip_name.
I'm not sure that I follow your point on the lack of "documentation for 
the slots of sensors_chip_name".

I followed the definitions in the c header file linked to below. Here is 
the header info:

> typedef struct sensors_bus_id {
>     short type;
>     short nr;
> } sensors_bus_id;
>
> /* A chip name is encoded in this structure */
> typedef struct sensors_chip_name {
>     char *prefix;
>     sensors_bus_id bus;
>     int addr;
>     char *path;
> } sensors_chip_name;
>
So I hope I'm supposed to do this.  I don't see any other way than to 
follow the API.

> It could work by luck in
> your C test case if the uninitialized name slot points to a null byte.
>
> __Martin

In the case of the cffi code, I use (with-foreign-object ...) to 
allocate the struct, so to test, I added a statement to my test function 
to initialize the path to an empty string before calling the parse 
function (which shouldn't touch the path slot):

> (defun test-sensors-parse-chip-name (string)
>   (with-foreign-object (name '(:struct sensors-chip-name))
>     (setf (foreign-slot-value name '(:struct sensors-chip-name) 'path) "")
>     (unless (= 0 (cffi-sensors-parse-chip-name string name))
>       (error "Failed to parse: ~A" string))
>     (with-foreign-slots ((prefix bus address path)
>                          name
>                          (:struct sensors-chip-name))
>       (format t "~%Prefix: '~A'" prefix)
>       (format t "~%Address: '~A'" address)
>       (format t "~%Path: '~A'" path))))

When I execute this I get the following without error:

> CL-LMSENSORS> (test-sensors-parse-chip-name "atk0110-acpi-0")
>
> Prefix: 'atk0110'
> Address: '0'
> Path: ''
> NIL
So I'm guessing that cffi requires I do some setup on freshly allocated 
objects.  I guess this makes sense, but isn't really communicated in the 
documentation.  All part of being a stranger in a "land of C" that 
values the speed of malloc over the certainty of calloc.

Looking at FOREIGN-ALLOC, it seems that :INITIAL-ELEMENTS can be a 
provided argument, but that functionality is buried when calling 
WITH-FOREIGN-OBJECT[S].  The whole topic seems ripe for an :INITFORM 
argument to DEFSTRUCT that when provided assigns initial values to slots 
after allocation.  I'm not sure if others would agree with such a 
feature, but it makes sense to me.

Martin, thanks again for your comments.  They triggered the right set of 
challenges to my ever lurking assumptions.

Cheers,
Elliott

>>>>>> On Thu, 26 Apr 2018 20:16:43 -0700, Elliott Johnson said:
>> Hello,
>>
>> If this is not the correct list to report a user error, I apologize and
>> will happily redirect my question where ever suitable.
>>
>> I've been using cffi to make a trivial library to libsensors
>> https://github.com/groeck/lm-sensors and am getting an odd memory fault
>> error when referencing a string value from a groveled struct.  After
>> hitting my head against a wall for a day and a half, I thought I'd ask
>> in case it's a simple mistake.
>>
>> Here's my information:
>>
>> Running a 64 bit linux install.  My local version of the libsensors
>> library is as follows:
>>
>>> elliott at desktop ~ $ file /usr/lib64/libsensors.so.4.4.0
>>> /usr/lib64/libsensors.so.4.4.0: ELF 64-bit LSB shared object, x86-64,
>>> version 1 (SYSV), dynamically linked, stripped
>>> elliott at desktop ~ $ sensors --version
>>> sensors version 3.4.0+git_83cafd29f28d463573750d897014ec7143217ae5
>>> with libsensors version 3.4.0+git_83cafd29f28d463573750d897014ec7143217ae5
>> I'm using a fairly recent version of sbcl built from source that
>> (apparently) passed all of the tests after the build:
>>
>>> CL-USER> (lisp-implementation-version)
>>> "1.4.6.140-f8d5864d0"
>> My version of cffi appears to be the latest:
>>
>>> CL-USER> (slot-value (asdf:find-system :cffi) 'asdf:version)
>>> "0.19.0"
>> My system uses cffi-grovel and defines the following structs:
>>
>>> (cstruct sensors-bus-id "sensors_bus_id"
>>>           (type "type" :type :short)
>>>           (nr "nr" :type :short))
>>>
>>> (cstruct sensors-chip-name "sensors_chip_name"
>>>           (prefix "prefix" :type :string)
>>>           (bus "bus" :type (:struct sensors-bus-id))
>>>           (address "addr" :type :int)
>>>           (path "path" :type :string))
>> These are based upon the definitions in sensors/sensors.h:
>> https://github.com/groeck/lm-sensors/blob/master/lib/sensors.h
>>
>> After asdf loading the system, the following groveler definitions are
>> created (had to dig these out of the cached build files):
>>> (cffi:defcstruct (sensors-bus-id :size 4)
>>>    (type :short :offset 0)
>>>    (nr :short :offset 2))
>>> (cl:defconstant size-of-sensors-bus-id (cffi:foreign-type-size
>>> '(:struct sensors-bus-id)))
>>> (cffi:defcstruct (sensors-chip-name :size 24)
>>>    (prefix :string :offset 0)
>>>    (bus (:struct sensors-bus-id) :offset 8)
>>>    (address :int :offset 12)
>>>    (path :string :offset 16))
>>> (cl:defconstant size-of-sensors-chip-name (cffi:foreign-type-size
>>> '(:struct sensors-chip-name)))
>> All of the above appears to be correct, so I proceed to load the library:
>>> (define-foreign-library libsensors
>>>    (:unix (:or "libsensors.so.4" "libsensors.so"))
>>>    (t (:default "libsensors.so")))
>>> (use-foreign-library libsensors)
>> No issues, so I define a c function from the c header file that parses a
>> sensor chip name-string into a struct:
>>
>>> (defcfun ("sensors_parse_chip_name" cffi-sensors-parse-chip-name) :int
>>>    "Parse a chip name to the internal representation.  Return 0 on
>>> success, <0 on error."
>>>    (orig-name :string)
>>>    (res (:pointer (:struct sensors-chip-name))))
>> Which is based upon the following header function declaration:
>>
>>> int sensors_parse_chip_name(const char *orig_name, sensors_chip_name
>>> *res);
>> Then I implement a simple call to print out the parsed values (not
>> including the bus):
>>
>>> (defun test-sensors-parse-chip-name (string)
>>>    (with-foreign-object (name '(:struct sensors-chip-name))
>>>      (unless (= 0 (cffi-sensors-parse-chip-name string name))
>>>        (error "Failed to parse: ~A" string))
>>>      (with-foreign-slots ((prefix address path)
>>>                           name
>>>                           (:struct sensors-chip-name))
>>>        (format t "~%Prefix: '~A'" prefix)
>>>        (format t "~%Address: '~A'" address)
>>>        (format t "~%Path: '~A'" path))))
>> The above when executed gives the following output before the error:
>>
>>> CL-LMSENSORS> (test-sensors-parse-chip-name "atk0110-acpi-0")
>>>
>>> Prefix: 'atk0110'
>>> Address: '0'
>> And once it tries to access the path variable, it gives the following
>> error (sorry a lot rolls off the screen):
>>
>>> Unhandled memory fault at #xE71B7.
>>>     [Condition of type SB-SYS:MEMORY-FAULT-ERROR]
>>>
>>> Restarts:
>>>   0: [RETRY] Retry SLIME REPL evaluation request.
>>>   1: [*ABORT] Return to SLIME's top level.
>>>   2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1002207F93}>)
>>>
>>> Backtrace:
>>>    0: (CFFI::FOREIGN-STRING-LENGTH #.(SB-SYS:INT-SAP #X000E71B7)
>>> :ENCODING :UTF-8 :OFFSET 0)
>>>        Locals:
>>>          #:.DEFAULTING-TEMP. = :UTF-8
>>>          #:.DEFAULTING-TEMP.#1 = 0
>>>          #:N-SUPPLIED-0 = 1
>>>          POINTER = #.(SB-SYS:INT-SAP #X000E71B7)
>>>    1: (FOREIGN-STRING-TO-LISP #.(SB-SYS:INT-SAP #X000E71B7) :OFFSET 0
>>> :COUNT NIL :MAX-CHARS 4611686018427387900 :ENCODING :UTF-8$
>>>        Locals:
>>>          #:.DEFAULTING-TEMP. = 0
>>>          #:.DEFAULTING-TEMP.#1 = NIL
>>>          #:.DEFAULTING-TEMP.#2 = 4611686018427387900
>>>          #:.DEFAULTING-TEMP.#3 = :UTF-8
>>>          ENCODING = :UTF-8
>>>          #:N-SUPPLIED-0 = 1
>>>          POINTER = #.(SB-SYS:INT-SAP #X000E71B7)
>>>    2: ((:METHOD TRANSLATE-FROM-FOREIGN (T CFFI::FOREIGN-STRING-TYPE))
>>> #.(SB-SYS:INT-SAP #X000E71B7) #<CFFI::FOREIGN-STRING-TYPE $
>>>    3: (TEST-SENSORS-PARSE-CHIP-NAME "atk0110-acpi-0")
>> My guess is I'm not defining the (:struct sensors-chip-name) correctly,
>> so that when sensors_parse_chip_name writes values into the struct, it
>> somehow overlaps into the path string.
>>
>> When looking at the source for sensors_parse_chip_name I've noticed that
>> it does not set or access the path value at all.  So I decided to write
>> a small c program to test the native behavior:
>>
>>> elliott at desktop ~ $ cat test-sensors.c
>>> #include <stdio.h>
>>> #include <stdlib.h>
>>> #include "sensors/sensors.h"
>>>
>>> int main( ) {
>>>
>>>    //sensors_init( NULL );
>>>
>>>    struct sensors_chip_name name;
>>>
>>>    int return_value = sensors_parse_chip_name( "atk0110-acpi-0", &name);
>>>    if ( return_value != 0 ) {
>>>      printf( "Failed to get a good return value." );
>>>      exit( return_value );
>>>    }
>>>
>>>    printf( "Printing the prefix: '%s'\n", name.prefix );
>>>    printf( "Address: '%i'\n", name.addr );
>>>    printf( "Printing path value: '%s'\n", name.path );
>>>    printf ("\n exiting..." );
>>>
>>>    exit( 0 );
>>> }
>>> elliott at desktop ~ $ gcc test-sensors.c -l sensors -o test-sensors
>>> elliott at desktop ~ $ ./test-sensors
>>> Printing the prefix: 'atk0110'
>>> Address: '0'
>>> Printing path value: ''
>>>
>>>   exiting...
>> No problems there, so I assume that the issue is all my lack of cffi-fu
>> training.  Any pointers (yes, pun intended) would be appreciated...
>> sorry for such a long read, but I hope it helps to have the background.
>> At the very least it helped me to frame the issue in my mind by writing
>> this.
>>
>> Regards,
>> Elliott
>>
>>




More information about the cffi-devel mailing list