-
Notifications
You must be signed in to change notification settings - Fork 7
Description
According to my memories from many-many years ago, so some details might be incorrect.
This is probably only relevant for 32-bit architectures.
There's a preprocessor hell going on which decides whether off_t is actually a 32 or 64 bit type.
Consistently with this, I think functions that use this type (e.g. truncate()) get either mapped to the 32-bit truncate or the 64-bit truncate64 symbol: it's also done using the same preprocessor hell in the standard include files. [Although I think it would be nasty for the standard include files to #define non-underscored names, e.g. it could even mess with programs that don't care about these methods and just use int truncate; locally to denote some irrelevant thing, the name clash being accidental. Perhaps this is where weak aliases or something like this come into play, I don't know.]
The exact behavior depends on certain defines, there are way too many constants like _LARGEFILE(64)_SOURCE, _FILE_OFFSET_BITS, __USE_FILE_OFFSET64 etc. that influence the behavior, and more generic ones such as _GNU_SOURCE might also set these. Their exact meaning is unclear to me, and so is which of these should an application set.
The way our interceptor works:
For the function names, if the risk of a system-wide conditional #define foo foo64 is a true risk, we can most likely work it around by #undef-ing first, so that the generated library has exactly the symbol we're looking for. Or run nm/objdump on the generated library to double-check its symbols.
Linking to the desired function in glibc is not a problem since we're using dlsym() to loop up the symbol with the exact given name.
The size of types in the parameters (and return value) is potentially quite dangerous. Maybe we accidentally include something that redefines off_t into off64_t, and thus create interceptor code that expects 64-bit values instead of 32-bit ones. We'll then get 32-bit values on the stack from the calling application, interpret them as 64-bit ones, place them as 64-bit ones on the stack when calling ic_orig_foo() which will interpret them as 32-bit ones. This easily results in "seems to work, but mysteriously misbehaves sometimes" situations, depending a whole bunch of factors (what the interceptor and the supervisor does with the parameters, what other parameters are there, endianness etc.)
We should properly study this area at one point, and take actions (like sizeof checks) to make sure the types in our interceptor always have the exact same size than in glibc, and as advertised in /usr/include.