1. 9

  2. 1

    What is a “Chunked-List”?

    base_ptr is of type *mut MaybeUninit, so I was skipping a few thousand whole chunks, landing very deep in uninitialized memory.

    The problem is that ptr.add takes a dimensionless unit (why? C programmers have known this was a mistake for decades, but we can’t fix it anymore! What’s rust’s excuse!?). You should be able to say something like ptr.add(8,bytes) but can’t. Why?

    The problem is the second line, &mut buf[0]. This creates a pointer that only has provenance for the first element of the array. Offsetting it and then trying to access another element of the array would be UB.

    How is that possible?

    1. 1

      How is that possible?

      Because &mut but[0] as *mut u8 creates a pointer to the first byte of the first element. Then add(1) advances one byte, yet the elements stored in the buf are of type i32 (4 bytes). You’re now pointing to the second byte of the first element instead of the second element.

      1. 1

        That’s what I would have thought but the claim as I understand it, is that it isn’t pointing to the second byte of the first element but the random number generator known as “UB”.

        A language designer says some behaviour is undefined because they don’t know the right answer to what something should be, so they want to leave an implementation free to have another answer if it is convenient.

        My question is what other answer does it need to have such that rust wants to pretend integers aren’t made of bytes?

        1. 1

          Perhaps an implementation could implement arrays such that they would “grow” in the same direction that the stack grows? So e.g. [0, 1] would put 1 at the top of the stack and 0 would be the next element on the stack (as in the next to pop from the stack after 1). And then on x86 (where the stack grows downwards) add(len) (where len is the length of an element of the array in bytes) would take you completely outside of the memory occupied by the array.

          1. 1

            That doesn’t sound very plausible. On x86, the stack pointer (%sp) points to the location of the top of the stack, but not the end of the stack memory. That memory region extends both directions (from %sp) on x86 (and actually most architectures). When people say “the stack grows down” they mean that pushing a value causes %sp to get a lower virtual address than what it had before.

            The reason for this is clear in the assembly; x86 has indirect addressing!

            mov %sp(0), %rax  // same as pop %rax;push%rax;
            mov %sp(1), %rax  // gets the previously pushed value
            mov %sp(2), %rax  // ... and so on

            NB: The above only works if you’re pushing pointers. If you push structures, then %sp(1) might actually point to the middle of a 128-bit value.

            Negative offsets, are usually used for “scratch” space, but some ABI have a “red zone” which is used by the callee, so you might see mov %sp(-17) past that zone if you actually spill registers. Most functions only have a few arguments (and they all fit into registers) so you don’t see stack allocation on x86 very much, but both memory regions are “valid” and “usable” if you understand the ABI.

            It seems much more likely to me this is something to do with aliasing.