Copying out a userspace pointer address

We’re working on execv and we’re having some trouble with the final portion when it comes to copying out the argv pointer values back onto the user stack.

As a preface, by the time we reach this problem we’ve already safely copied in all arguments from the user stack, loaded them into one large string (including the necessary null terminator offsets to keep everything 4-byte aligned), copied that string out to the user stack, and then copied out an empty char ** argv array with a NULL value for the final element to the user stack. Note that this array size is the number of arguments + 1 for the NULL element at the end of the array.

Initially we tried filling this argv array with the proper userstack addresses pointing to each string argument before copying out, but apparently (as noted by two different TA’s), those addresses are “lost in translation” during the copyout process (I’d be interested to hear the “why” on this).

For this reason, we instead are trying to copyout the “empty” argv array first, and then 1-by-1 copyout the proper char * address (the user stack address of the first character of each arg) to each 4-byte element in argv.

The problem is that after the copyout, the address value that ends up in argv[i] on the userstack doesn’t match the value of the src argument that we pass to copyout. We verified this by printing the address value right before the copyout call, and then right after doing a copyin of 4 bytes at that same address we just copied out to see the difference. If we copy out the address 0x7fffffe0, when we copy back in to verify that the addresses are the same (i.e. the same value made it onto the user stack), we get back 0x2f746573. Another example is copying out 0x7ffffff4, and getting back 0x44616c65.

The error thrown after our call to enter_new_process is a TLB miss on load error. The vaddr is one-off from one of the values that’s (incorrectly) copied out…

Fatal user mode trap 2 sig 11 (TLB miss on load, epc 0x401480, vaddr 0x44616c64)

To explain our copyout call in more detail, our src argument is a userptr_t that contains the address value that we want to copy out, which we cast to a char * (behavior is the same if we don’t cast). This points to the relevant arg’s first char address on the user stack. The destination arg is a userptr_t that is set to the first byte of argv[i] on the user stack, and the size argument is set to 4 bytes.

We’re not sure how to proceed on this. I’m definitely going to try and get a TA’s help tomorrow, but with everyone in crunch time it’s sometimes hard to get help. Can anyone see anything we’re doing wrong here? Please let me know if any additional info would be helpful. Thanks for taking the time to read!

There’s no reason that this shouldn’t work.

The tricky thing about exec is that the addresses won’t work until you relocate the arguments and the argv array. That’s why it’s hard to debug. So testing things from the kernel can be hard.

I’m confused by your copyout example. copyout works :slight_smile:. Are you getting the arguments mixed up somehow?


Thanks for the quick reply!

I will definitely try this again. And yeah, I know it’s something we’re doing wrong lol, not trying to blame copyout :wink:

To my knowledge I’m not getting the arguments wrong. I’m basically saying, “At the address where argv[0] is on the userstack, fill those 4 bytes with the userstack address 0x7fffffe0, because this is the userstack address of the first character of that argument and we want to point there”.

So to make sure I’m understanding what you said should work, if I initialize a char ** argv array in kernel space, fill it’s elements with addresses (i.e. argv[0] = (char *) 0x7fffffe0) and then copyout the entire argv array… It will still correctly point to those addresses when it makes it to the userstack?

There’s nothing special about pointers in memory—they’re just bytes. So yes: they get copied like everything else, bit by bit.


Okay I will give this a try, thanks for the help!

This fixed the issue. I’m not sure what I was doing before when I tried the first time… This is why I need to get better at taking breaks :slight_smile:

Thanks @gwa , enjoy the break!


I am a little confused about the way copying arguments was supposed to work. My group did something similar to Alex’s group, where the char * pointers were copied out one-by-one to the user stack.

However, our approach was different than Alex’s initial approach - instead of copying out an empty char ** argv array, we tried to individually copy out each char *, already pointing to the correct address. This didn’t work, and interestingly we got the same exact TLB miss on load error as Alex (as depressing as it sounds, I know exactly what test he was using to test his execv. 0x7FFFFFE0 and 0x7FFFFFD4 are burnt into my brain :slight_smile: ).

Our method only worked when we made each char * an element of a char ** array, and copied out the entire char ** array. My confusion is centered around why my initial approach didn’t work. If the char * pointers are aligned properly, and pointing to the correct values, why can’t we just copy them out one-by-one? Is it just strictly required that a char ** array is copied out due to the way a new user program deals with the memory on the user stack?

I also noticed that some completed execv by copying out a single buffer that contained the characters and the argv array. Is it safe to assume the only way that works is if the buffer is of type char ** ?

All that matters is that the new program can follow the char ** argv to find a NULL-terminated char * array of pointers to NULL-terminated strings. You can definitely accomplish this by setting up one buffer, arranging things properly, and then copying it out all at once.

And the type of the buffer doesn’t really matter. It’s all just bytes. Types are really only important to the compiler.

Literally all I changed was copying out all of the arguments at once, instead of one at a time. Could it just be finicky?

I mean, it’s a computer—it’s not finicky ;-). Maybe there was an off-by-one error or some other problem with your loop copy-out?