2

I have this code in C11 (skipping references to pre-declared variables and functions):

        // [...]
        char *old_buf = it->buf;
        shift += it->buf_size;
        it->buf_size *= 2;
        char *tmp = realloc (it->buf, it->buf_size);
        if (!tmp) {
            log_error ("Memory allocation error.");
            return -1;
        }
        it->buf = tmp;
        size_t reloc_off = it->buf - old_buf;
        it->cur += reloc_off;
        it->tok += reloc_off;
        it->lim += reloc_off;
        it->mar += reloc_off;
        // [...]

GCC with -Wextra sends this warning:

parser_common.h:104:36: warning: pointer may be used after ‘realloc’ [-Wuse-after-free]
  104 |         size_t reloc_off = it->buf - old_buf;
      |                            ~~~~~~~~^~~~~~~~~
parser_common.h:97:24: note: call to ‘realloc’ here
   97 |         char *tmp = realloc (it->buf, it->buf_size);
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I would assume that it's safe to overwrite it->buf after I have verified that tmp is not NULL, and that until reassignment it->buf has become garbage.

Why the warning then? Is it benign or is there an actual risk?

EDIT: the reasoning for calculating the offset is that I have some markers on the old buffer, to mark relative positions in a text. The buffer is filled with user data and may need to be reallocated when the input is larger than the initial allocation, as I am doing here. When I do the reallocation, the markers need to be moved to their relative positions to the new buffer address.

13
  • 2
    On a different note, if realloc fails, the memory pointed to by it->buf will still be valid (and needs to be passed to free sooner or later). But the value of it->buf_size will be wrong. Commented Nov 24 at 17:16
  • My guess is that it's complaining about using old_buf, but I need to reference the old address to calculate reloc_off without actually using the data. Commented Nov 24 at 17:17
  • There's two problems with that subtraction: old_buf might not be valid any more, and you can't subtract pointers to different objects. Commented Nov 24 at 17:18
  • 2
    Either realloc() doesn't move the buffer, in which case the offset is 0, or it moves it and you can't do the subtraction. Commented Nov 24 at 17:19
  • 1
    It sounds like what you really need to do is store offsets in other variables/structures, rather than pointers, so you don't need to adjust all the other pointers. Commented Nov 24 at 17:20

2 Answers 2

8

The warning is not about it->buf, but rather its old value, which you saved in old_buf, and are using for some pointer arithmetic. GCC knows that using an old pointer after realloc can lead to UAF and warns you about it.

It does not make sense to do pointer arithmetic like you are doing, if not for some educational/research purposes. If we want to be pedantic, it should also be invalid according to C standard as the two pointers no longer point to the same object. See the answer here: Subtraction between pointers of different type. realloc is free to allocate memory wherever it wants, checking the "distance" wrt the previous allocation address isn't something you should be concerned about.

Sign up to request clarification or add additional context in comments.

7 Comments

See my added edit in the original post. I do need to calculate offsets of markers I previously assigned.
I do need to calculate offsets of markers I previously assigned That doesn't change the fact that the code invokes undefined behavior. Save the offsets another way - one that doesn't invoke undefined behavior. Unless you like having inexplicable bugs appear in code that appeared to work for years but than "randomly" started failing.
The OP could use something like size_t cur_off = it->cur - it->buf; before the realloc, and it->cur = it->buf + cur_off; afterwards. Alternatively, they could store the offset in the structure instead of a pointer. Then the realloc code wouldn't have to do any adjustments at all.
Find me a platform where this is broken.
The warning message is slightly misleading. The squiggle should arguably be just under old_buf, not the whole subtraction.
That got me misled indeed.
@Joshua It's not modern or common, but inter-object pointer subtraction wouldn't work in Symbolics C.
3

old_buf contains the address of memory that was passed to realloc. If realloc allocated new memory and freed the old memory, then it->buf - old_buf uses a pointer after its memory has been freed. In particular, the subtraction is not defined because C 2024 6.5.7 defines subtraction of two pointers only for pointers into the same array (including a single object [which behaves as an array of one element], a pointer to the end position that is just beyond the end of the array, and/or characters pointers to the bytes of an object). it_buf and old_buf would not be pointing into the same array.

If you can require your target platform to have a flat address space and can require conversion of pointers to integer types to work in the ordinary way, then you can relocate your pointers in a defined way by using integer arithmetic instead of pointer arithmetic. When saving it->buf, convert it to intptr_t:

intptr_t old_buf = (intptr_t) it->buf;

When calculating reloc_off, use:

intptr_t reloc_off = (intptr_t) it->buf - old_buf;.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.