9 years, 9 months ago.

writing to uint32_t value within struct causing crash/hang

Hi There,

I've got a bug that I suspect is fairly easily solved by someone who knows this sort of thing better than me.

My Setup : I am using a FRDM KL25z board, using the USB OTG connector to try to make contact with a USB camera. Certain parts of this system work, but the issue is causing the software to hang up in certain areas.

I’m using the online IDE, and importing the USBHostPTP library-http://developer.mbed.org/users/jakowisp/code/USBHostPTP/

I’ve got a decent background in this sort of thing (i.e. banging my head against bizarre errors) but the online IDE doesn’t give me much control in diagnosing or customizing things.

The issue :

I have a C struct with a mix of uint16_t, uint32_t, and some custom object type variables. The uint16_t’s are declared first within the struct declaration, then the uint32_t’s, and then the custom objects.

I am reading data of different types from a stream represented by a uint8_t * pointer. To do this I am incrementing a pointer within that stream, and grabbing casts of the correct type for my pointer. For example, I grab a uint8_t, increment the pointer by 1, then cast the pointer’s value to a uint16_t to get the next value, increment the pointer by 2, etc. I am being vague on the details of the dereferencing, but this part works, code below :

Code snippet

void USBHostPTP::ParseDeviceInfoDataBlock(void *ptp,uint8_t *buffer,uint16_t length){
     uint8_t *currentPtr;
     USBHostPTP *dev=(USBHostPTP *)ptp;
     
     currentPtr=buffer;
     dev->deviceInfo.standardVersion=*((uint16_t *)currentPtr);
     currentPtr+=2;
     dev->deviceInfo.vendorExtensionID=*((uint32_t *)currentPtr); //This is where it all fails. Casting to uint16_t makes it work
     currentPtr+=4;
     dev->deviceInfo.vendorExtensionVersion=*((uint16_t *)currentPtr);
     currentPtr+=2;
     
     currentPtr+=dev->deviceInfo.vendorExtensionDesc.FillString(currentPtr);
          
     dev->deviceInfo.functionMode=*((uint16_t *)currentPtr);
     currentPtr+=2;
     
     currentPtr+=dev->deviceInfo.operationsSupported.FillArray(currentPtr); //fails again here as this function uses some uint32_t casting as well
 
etc ... 

Struct Declaration snippet

/// PIMA 15740:2000 standard 5.5.1 Table 6        
typedef struct {
    uint16_t            standardVersion;
    uint16_t            vendorExtensionVersion;
    uint16_t            functionMode;
    uint16_t            padding;    //gave it a whirl, no dice
    uint32_t            vendorExtensionID; 
    PIMAArray<uint16_t> operationsSupported;  // 2 pointers (uint32's )
    PIMAArray<uint16_t> eventsSupported;
    PIMAArray<uint16_t> devicePropertiesSupported;
    PIMAArray<uint16_t> captureFormats;
    PIMAArray<uint16_t> imageFormats;
    PIMAString          vendorExtensionDesc;    //an 8 and 2 pointers (32's)
    PIMAString          manufacturer;
    PIMAString          model;
    PIMAString          deviceVersion;
    PIMAString          serialNumber;
} DeviceInfoStruct;

This works until I try to load in a value of type uint32_t to my struct. The variable in question is of type uint32_t (vendorExtensionID ) , and I can load in a value of type uint16_t or uint8_t (either from pointer casting or from arbitrary temp variables) but loading in a variable of type uint32_t (either from a temp or from casting the pointer) causes the program to hang. It is not feasible for me to work around this, and I would not have faith in the system if I can’t get this resolved.

Possible issues that come to mind are :

Struct memory alignment problems (my best guess). I am not calling #pragma pack at all, and I have ordered the values in increasing size, but that does not solve the issue. I suspect that the custom objects, which have mixtures of uint32_t, uint8_t, and arrays within them could be causing some memory boundary issues for the struct. If that is the case, I need to figure out how to properly pad things to resolve this. I got this idea from http://developer.mbed.org/forum/bugs-suggestions/topic/4264/

I think that this may be affected by the PIMAArray<uint16_t> types within the struct. I am not 100% clear on what is going on with those (didn't write them), but looking into the class the two private local variables are : uint32_t numberOfElements; /Number of elelments stored in the array TYPE *elements; /Pointer to elements storage I suspect that the <uint16_t> is being subbed in for the TYPE * though I suppose that it should still be a uint32_t since it's a pointer. In that case, the size of the PIMArray objects should be 64 bytes, which is even.

For the PIMAString's they hold : length=0; uint8_t StringChars=NULL; 32 bit pointer vals=NULL; 32 bit pointer

Some sort of compiler problem that does not like uint32_t- not sure what it would be, but I am using 3rd party libraries. These don’t seem to have their own declaration or redefinition of uint32_t, and uint32_t's are used elsewhere without (apparent) issue

A memory overwrite issue- I don’t think it’s this since I can write values into that variable in the struct, just not of type uint32_t, as well as later on in the array, but the failure always occurs where uint32_t’s are loaded into anything in that struct, including into private variables of objects held within the struct

It is also worth noticing that I get no compiler warnings when loading in the wrong type (ie uint8_t to a uint32_t) to that struct’s value, which could be consistent with the compiler’s inability to properly pack and manage the struct

My hope is that there is some sort of simple answer here such as :

-call XX to properly pack the struct automatically -press YY button to make the compiler function better

Thank you, and I hope I've been clear enough here! -Shibbs

     currentPtr=buffer;
     dev->deviceInfo.standardVersion=*((uint16_t *)currentPtr);
     currentPtr+=2;
     dev->deviceInfo.vendorExtensionID=*((uint32_t *)currentPtr); //This is where it all fails. Casting to uint16_t makes it work
     currentPtr+=4;
     dev->deviceInfo.vendorExtensionVersion=*((uint16_t *)currentPtr);
     currentPtr+=2;
     
     currentPtr+=dev->deviceInfo.vendorExtensionDesc.FillString(currentPtr);
          
     dev->deviceInfo.functionMode=*((uint16_t *)currentPtr);
     currentPtr+=2;
     
     currentPtr+=dev->deviceInfo.operationsSupported.FillArray(currentPtr); //fails again here as this function uses some uint32_t casting as well
 
etc ... 

Struct Declaration snippet

How does it fail? That second statement , retrieving vendorExtensionID does not seem to be correct

posted by Martin Kojtal 13 Feb 2015

How does receiving the vendorExtensionID seem any less correct than for the standardVersion? As for the way it fails, the CortexM0 just hardfaults (stops running) when it runs into this issue, see my comment on the accepted answer for some more on this. Thanks, Shibbs

posted by Stephen Hibbs 13 Feb 2015

1 Answer

9 years, 9 months ago.

Memory allignment would also be my first guess, although you say you don't pack it or anything similar.

Still easy way to figure it out: Can you print the memory addresses of the different variables in the struct that are accessed in your function? (And then especially the 32-bit one).

I also wouldn't expect you to get warnings from the compiler, loading different integer sizes into each other is acceptable, it is up to programmer to make sure it all fits nicely. And an 8-bit into a 32-bit will never be an issue.

Accepted Answer

Hi Erik,

Yep it ended up having to do with the alignment and the size of the PIMAString object, which was coming up as size 12. I padded it (added some private variables AND had to initialize them) until I got it to size 16 (double word), and now it works!

I printed out the various sizes and got :

size of struct: 132 size of vendorExtensionID: 4 size of operationsSupported: 8 size of vendorExtensionDesc: 16 This was originally 12

For those who may read this in future, the issue was that the original project was done with a CortexM3 chip, which does have some amount of memory alignment hardware, unlike the CortexM0 (on my chip), which will hardfault and stop running if there is any alignment problem. The M0 aligns on words (8 bytes) and so changing things to be on word or double-word alignment solved the issue. Note that the padding I added with the uint16 value was also important. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABFAIGG.html

A very frustrating thing to run into, but hopefully this is clear enough to help someone else out!

Thanks, Shibbs

posted by Stephen Hibbs 13 Feb 2015