2

EDIT: modified void * to be uint8_t * values. Problem still persists.

EDIT: The problem was a simple variable overflowing and had nothing to do with integer promotions.

I tackled a bug in that simplified piece of code. The types are the same as in the origin source code.

unsigned int entrySize;    // entrySize is 288
int startIndex, endIndex;  // both are 24536838
uint8_t *pStartAddr;          // valid initialized pointer (0x34f1e40)

/*Mystery begins...*/
uint8_t *curr_addr = pStartAddr + entrySize * startIndex;
while (curr_addr <= startAddr + entrySize * endIndex)
{
    externFunc(curr_addr);
    curr_addr+=entrySize;
}

In quick glance this code seems obvious enough, excluding the weird type selection.

However in one of our crashes it seems that curr_addr gets an invalid pointer. My hunch was that there is a problem with entrySize * startIndex since their multiplication sets on the 32th bit, and the having a startIndex and endIndex as signed types might confuse the compiler of the desired value to use.

After changing their types the unsinged long, the problem was solved. but I am unable to figure out what exactly is wrong.

I am working on 64bit machine, x86_64 CPU, gcc (GCC) 4.8.5 20150623 and linux red hat distribution (version 4.8.5-28)

I assumed that the problems start happening when the calculation above sets on the 32th bit of entrySize * startIndex. However, it still worked when I used the first startIndex value that had the 32th bit turned on. It also displayed

My questions are:

  • is the value result of int*unsigned int considered as signed or unsigned? what is the rank of the result type? multiplication can probably overflow to 8 bytes types and I assume the compiler prevents losing precision, right?
  • startAddr is a void*. it is then added to whatever value&type calculated in first question. is void* considered signed or unsigned? my hunch is of course an unsigned value, but I can't back it up.
  • What Integer promotions takes place in startAddr + <<result>>.
  • Can our while statement never halt (in practical time)? If the right side of the inequality is a signed a number (in width of at least 8 bytes), is the left side (curr_addr) will be promoted as signed number as well, result in the endless loop?
  • Step by Step explanation would be most welcome :)

I read those whatever contains in those links, and still remained clueless:

  1. https://www.oreilly.com/library/view/c-in-a/0596006977/ch04.html
  2. Integer conversion rank of signed and unsigned int
Aviv
  • 414
  • 4
  • 16
  • @IgorGalczak all the values are well defined, don't worry :) the startAddr is actually an address from a global symbol (&variableName from the data segment) – Aviv Mar 08 '19 at 13:48
  • 1
    If this code were "well defined" you wouldn't have asked that serious of questions about what's going on with that "well defined" code. The code is mixing signed and unsigned integer arithmetic that's prone to undefined behavior on overflow and combining it with calculations using a `void *` pointer. – Andrew Henle Mar 08 '19 at 13:53
  • @AndrewHenle The values I referred to are the variables used in the flow. The pointer set in startAddr is valid. You are right the the thigns happening afterwards are far from being well defined. I am still looking for a good explanation for me, and for the sake of others like me in the future. – Aviv Mar 08 '19 at 13:56
  • 1
    Possible duplicate of [Pointer arithmetic for void pointer in C](https://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c) – Jean-Baptiste Yunès Mar 08 '19 at 13:58
  • 1
    You can't use any form of arithmetic with void pointers. – Lundin Mar 08 '19 at 15:10
  • Guys, give break to the void * thing. It has to determine a default size, and it must be byte sized. besides, the problem recreated when I converted all pointers to uint8_t *. anyway, the problem was about overflowing and not weird integer promotion as I thought before first, thanks to @AndrewHenle answers – Aviv Mar 08 '19 at 15:30

2 Answers2

5
  1. The behaviour of pointer arithmetic on a void* is undefined.

  2. Pointer arithmetic is only valid within arrays. Note that you can set a pointer to one past the final element of an array, but don't attempt to dereference it. This rule also applies to objects which can be regarded for the purpose of the rule as single element arrays.

(1) is certainly not being obeyed in your code (didn't your compiler warn you - if not, bin it), (2) might not be. Somewhat facetiously then (on my part), your specific questions are not relevant.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    "The behaviour of pointer arithmetic on a void* is undefined" It's not even that, it's a constraint violation, meaning it can't compile cleanly. C17 6.5.5 and 6.5.6 respectively. – Lundin Mar 08 '19 at 15:13
  • It does compile, perhaps some compilation allows it, ill give it a look – Aviv Mar 09 '19 at 10:53
2

multiplication can probably overflow to 8 bytes types and I assume the compiler prevents losing precision, right?

That is one badly incorrect assumption. Per the 3.4.3 description of undefined behavior from the C standard (bolding mine):

3.4.3

1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

Integer overflow is the very example of undefined behavior used in the C standard itself.

And 288 * 24536838 equals 7066609344, which is well beyond the capacity of a 32-bit int, be it signed or unsigned, thus invoking undefined behavior.

So no, the compiler does not "prevent losing precision". Quite the opposite, in fact.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • Wow, about 7066609344, you are completely right! I used windows 10 calculator, but I forgot the bits are 0-based! the 32 bit I mentioned is actually the 33th one. nice, I need to digest that information for a bit – Aviv Mar 08 '19 at 14:23
  • oh well, After realizing that I found out this is just an overflow issue. indices 14913080 and 14913081. 14913081 * 288 results in value above 32bit, thus leading to overflow incorrect pointer direction. Your answer was the closet, so thank you very much – Aviv Mar 08 '19 at 15:07