Structure Alignment

I sometimes forget what I have known before. Does anyone know if there are any structure alignment requirements when using the Rack SDK for module development? I had a bizarre bug that I finally got rid of by changing the structure byte size. The member I removed was an unsigned char[10] . I’m wondering if the 10 byte size object was causing alignment issues. This member was not even being used, strengthening my suspicion about the structure alignment. I know in certain development projects in the past structure alignment was very important, but I cannot remember what determined that. I just did a test and added the unsigned char[] back in and if I use a non power of 8 dimension, the bug comes back. So, I’m thinking that this is a structure alignment issue.

It’s impossible to know without seeing more details.

// make it a power of 8 for structure alignment purposes
#define MAXSHORTSTRLEN 16 
#define MAX_CIRCLE_STATIONS 12
#define MAX_HARMONIC_DEGREES 7

struct CircleElement
{
	int chordType=0;  
	float startDegree; 
	float endDegree;
	unsigned char DummyMember[MAXSHORTSTRLEN];  // this is the one causing problems
	Vec pt1;  //vertices of annular ring segment
	Vec pt2;
	Vec pt3;
	Vec pt4;
	Vec radialDirection;
	int ButtonParamIndex;
	int ButtonLEDIndex;
	int LightKeyIndex;
	int DegreeIndex=-99;  // correspondence between degree element and circle element.  May not have one, so -99
};

struct CircleElement Circle5ths[MAX_CIRCLE_STATIONS];

The error occurs elsewhere in the code, messing up an int array value.

Meander is up to about 5K lines of code. Much of this comes from my Meander for Windows program written starting in 1988 (for DOS). I’'ve gone through it now and replaced all “unsigned char” byte integer usages with “int”. Back in the day it was important to try to make variable sizes as small as possible, thus the use of unsigned char[].

By the way, either a char[] or an unsigned char[] causes the problem. As I mentioned this particular struct member was never actually being accessed elsewhere in the code, so it is removed now.

I’ll be 68 in a couple of months, so I’ve forgotten some aspects of my software development career.

just a thought, maybe run a sanitizer against it and see if perhaps you’re experiencing a one-off indexing issue that’s being disguised by a larger array size?

typically adding -fno-omit-frame-pointer -fsanitize=address should enable the sanitizer.

I assume that goes in the make file or as command line arguments to the make file? Yes, I am concerned about the possibility of having an out-of-bounds array write that is difficult to track down.

Thanks for the suggestion.

add it to your CXXFLAGS, and it might make sense to add -fsanitize=address to your LDFLAGS. note that some environments might balk at loading a dylib with sanitization when rack itself isn’t built with sanitization, but typically there’s a startup message about it and an environment variable to set to make sure that the sanitization gets set up before rack starts.

in my case, for macOS, it’s usually something like:

$ DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ./Rack -d

but if you receive a warning, it should tell you exactly what to do.

I build under Windows and the MINGW64 bash shell with the following make call:

RACK_DIR=“D:/Rack-SDK-1.1.5/Rack-SDK” make install

from within my plugin development folder. Do you know if I can pass the sanitize flags as arguments on the above line? Or do I have to edit makefile or plugin.mk ? If someone else knows how to do this specifically for Windows, please let me know.

This seems to be an important option to detect problems before runtime.

Thanks.

you can either add them to your command-line make:

$ RACK_DIR=“D:/Rack-SDK-1.1.5/Rack-SDK” make CXXFLAGS="-fno-omit-frame-pointer -fsanitize=address" install

I prefer to add them to the Makefile, since when I’m developing I’m also compiling with -g. you can likely add something like:

ifdef DEBUG
        CXXFLAGS += -g -fno-omit-frame-pointer -fsanitize=address
        LDFLAGS += -fsanitize=address
endif

and then make DEBUG=1

Thanks, that worked from the command line. But the make outputs the following error:

/Meander.cpp:1:10: fatal error: rack.hpp: No such file or directory 1 | #include <rack.hpp>

Any idea why this error occurs with the sanitize flags?

So, am I understanding correctly that the sanitize flag causes code to be inserted to do address checking at runtime? Does it do array bounds checking? Is there any report of problems or does it just prevent problems, at the expense of some extra code?

it looks like your RACK_DIR isn’t being set correctly. check your make arguments.

as for what the sanitizer does, there’s some great documentation both on wikipedia and the main documentation. bounds checks, leak checks, memory access checks, etc, all output for you on the command-line when you execute.

The same RACK_DIR is being used with and without the sanitize flags, but the error only occurs with the sanitize flag. I’m probably doing something stupid.

you can always just modify the Makefile itself.

Yep. Thanks. I’ll do that tomorrow.

I added the sanitize compile flags to my makefile and redirected stdout and stderr to a file. This file contains 5667 lines. Just for clarification on how to use sanitize, does this compile output file have any debug benefit in itself, or is it just a log of the sanitize code being inserted into the program? Actually, looking closer at this output, all lines were generated in the link phase and seem to be missing asan library functions. This was without the LDFLAGS set. If I set the LDFLAGS, the output file is just a dozen or so lines with the last 3 indicating an error:

D:/msys64/mingw64/bin/…/lib/gcc/x86_64-w64-mingw32/9.2.0/…/…/…/…/x86_64-w64-mingw32/bin/ld.exe: cannot find -lasan collect2.exe: error: ld returned 1 exit status make: *** [D:/Rack-SDK-1.1.5/Rack-SDK/compile.mk:52: plugin.dll] Error 1

Any idea why -lasan address sanitize library cannot be found by MINGW64 ?

Again, does the build log give any useful sanitize information, or does that only occur during Rack and the plugin DLL exection? I’m reading the documentation links you posted so I can interpret the runtime “instrumentation”, etc.

I appreciate your help on this. It seems to be an important tool for developing reliable plugins.

Thanks

My understanding is that the sanitizers are not available for Windows. (At least they didn’t use to.)

In any case, if you get them working (maybe by using Linux or macOs for the builds), they are not build time analyzers, you need to build an executable that you run and the sanitizers detect issues at run time. They worsen the performance of the code a lot, so you of course don’t want to be distributing binaries for users with the sanitizer code included. (Unless for testing purposes on other systems, but usually there shouldn’t be a need to do that, issues would typically be found on the development system already.)

That’s good to know! If anyone else has successfully used AddressSanitizer with Windows and for Rack SDK, please let me know.

I just found a part of my code where I was accessing an array out of bounds. Hopefully that was the source of the original memory corruption bug. I’m still testing this as well as going through all of my code looking at array bounds.

Thanks.

hm. that’s unfortunate. if your code is open source, and you post a url, I’m happy to compile and run on a platform that supports sanitizers and send you the output.

1 Like

Thanks. For right now it appears that resolving my out of bounds array write also resolved the bug I was chasing. I can no longer reproduce my original bug.

I’m very carefully going through all of the 4000+ lines of module code to do a manual sanity check on array elements writes.

Maybe prior to releasing the module I will take you up on your offer. Thanks!

let me know - my email address is available on my GitHub profile and with every module I’ve released. happy to help out where I can!

Theses out of bound accesses can be a nightmare to track… One possible workaround is to replace C-style arrays with std::array, and accessing them with the ->at()method, which will do the boundary check for you.

1 Like