"you try it out on the child and if it dies you will know not to do it on the parent." spectacular.
@rian0xFFF Жыл бұрын
i'm glad you're back
@JacobSorber Жыл бұрын
Thanks. Life's been super busy, but hopefully settling down a bit soon.
@prospermatkovic555923 күн бұрын
Nice! I think that second method is used in some unit testing frameworks like 'check' for c. Might revisit that on some future occasion. Completely unrelated, but I would like to see future videos on how to: 1. share state between units, 2. avoid circular dependencies, 3. some sloppy programming habits (for example use of globals..), 4. reentrancy and thread safety
@captainpingou930 Жыл бұрын
I like the way the safest way to check a ptr is to sacrificing a child 😂
@Xiterlide Жыл бұрын
basically the technique: 1) if you feel something might be dangerous, give it to your child 2) iff it kills itself with it, it's dangerous.
@CodeActice Жыл бұрын
You would be sued for putting your child's life in danger in the Kernel Court!
@miteshsharma3106 Жыл бұрын
Would love to see the video about creating pointers with specific privileges (read only, write only, etc)
@DemonSlayer627 Жыл бұрын
I think a better way would be using msync to check if a pointer is mapped, a way would be bool valid_mem(void *loc) { size_t page_size = sysconf(_SC_PAGE_SIZE); size_t page_mask = ~(page_size - 1); void *snapped = (void *)(((size_t)loc) & page_mask); int ret = msync(snapped, 1, MS_SYNC); if (ret < 0) { if (errno == ENOMEM) { // panic("Invalid mem"); return false; } } return true; }
@01rnr01 Жыл бұрын
What if the memory is mapped but mprotect'ed? What would be the best way to check if the read is not going to issue the protection?
@johnpawlicki1184 Жыл бұрын
Of course just because we can access a pointer does not mean that it is a good idea to read/write it. I like your comment up front. Code the project properly, not just to get it to comkpile and run. Good video.
@alejandropoirier6493 Жыл бұрын
Love this channel, thanks for such quality videos!
@unperrier5998 Жыл бұрын
Thanks Jacob. Maybe next you could do a video using mmap() and/or madvise() to do the same thing.
@beezball38 Жыл бұрын
would be a bit intensive to keep copying the entire address space for larger programs, still neat
@SimonJentzschX7 Жыл бұрын
Thanks for the great video. I would love to learn more, how the memory mapping works after fork. Is the mapped memory copied for the child-process? or only once accessed?
@paherbst524 Жыл бұрын
This is clever. I like it.
@AK-vx4dy Жыл бұрын
Neat trick
@Bruno_Haible Жыл бұрын
Two other approaches are: Read the /proc/self/maps file, which contains the ranges of valid addresses. Or use the mincore() system call.
@Rai_TeАй бұрын
The way you implemented your test will lead to strange behaviour (up to crashes) ... the devil is (as often) in the detail ... you call exit() in the child to indicate that things did work ... now, exit() has a sideeffect that is often not taken into account: exit() flushes IO-buffers. So, if in the parent, you did a printf("hello") then you do your test ... the test works ok ... then you print(" ") in the parent, you will see hellohello on your terminal ... one hello being from the child flushed out with the exit ... same applies to all open files. So, if you want to fork() a sandbox process please (PLEASE) call _exit() rather than exit() _exit() does not flush buffers.
@danielrhouck Жыл бұрын
Two questions: 1. Doesn’t this cause problems if `ptr` points somewhere Interesting like shared memory, an `mmap`ed file, a DMA device, etc.? 2. Isn’t there a way to actually look at the current maps and see what `ptr` points to, instead of actually trying it?
@maxaafbackname5562 Жыл бұрын
Pointers to hardware are differently mapped than pointers in the process space.
@danielrhouck Жыл бұрын
@@maxaafbackname5562 Can you expand on that? How so? I assume you can’t just `mmap` them but I thought they were still, as far as C was concerned, just pointers
@maxaafbackname5562 Жыл бұрын
@@danielrhouck as far as the concept of the pointer itself, there is no difference for the language and the CPU instructions. The addresses in a process are virtual addresses, not fysical addresses. These virtual addresses are handled/controlled by the Menory Managment Unit. This virtual addresses are mapped by the MMU to fysical addresses. (This includes things like paging and virtual memory.) Because of this mapping, addresses in a process are the same virtual address (as seen by the "user") in a spawned (child) process after a fork(), but are at different fysical addresses. The parent process memory itself is not shared with the child, only the addresses. (Memory between threads is shared, but fork() is about processes.) That's why the trick with the child process works. If the address is valid, the write is done on the same virtual address, but at a part of memory that is only of the child process, not the parent process. (The child has the same ranges of valid addresses as the parent process.) From the point of view of the parent process, the write is never done. The wite is only done in the memory of the child process, not the parent process. For hardware addresses, the trick with the child process does not work because that mapping from virtual addresses to fysical addresses does not exist or is different. The write to that (hardware) adress can't be tricked because if the write is valid, the write is done. And a double write to a hardware address results mostly in an unwanted result/hardware state. In Linux there are virtual, logical and fysical addresses. But I don't think the explanation of that make much difference for this explanation. Also the case of things like hardware virtualisation etc.
@maxaafbackname5562 Жыл бұрын
Maybe good point about shared memory? The pointer/code does not knows it is pointing to memory thar is shared between parant and child process. If the process scheduler decides that the parent process is run before the child process, after the fork(), the last of the two writes to the same (shared) menory, is not the write (withe the value) you want!
@danielrhouck Жыл бұрын
@@maxaafbackname5562 I know why the `fork` trick mentioned would work in “normal” cases. l think you’re saying the same thing I was; with a pointer to a hardware device the `fork` trick doesn’t work.
@cjreek8 ай бұрын
I mean it's a fun(ny) way to teach some things about fork, pointers and memory (protections), but (and I'm glad you said this at the beginning) it can't be emphasized enough that this is one of the worst things I have ever seen and if you're using this in anything that's not a test programm to mess around you're going straight to programmer hell :D
@marbens7 ай бұрын
6:18 Not any char value, but any unsigned char value. On most x86 systems char is signed. On most ARM systems char is unsigned because it's faster.
@andrewporter1868 Жыл бұрын
If you could do a video (and maybe a tutorial) on catching exceptions using GNU C and inline assembler for the Windows x86 implementation, that'd be great (it's very annoying to test that your code correctly fails to read or write to write-only or read-only pages via commenting out code after guaranteeing that you get a STATUS_ACCESS_VIOLATION exception; can't find much about it either).
@abrarmasumabir3809 Жыл бұрын
Sir plz make a video on msvc debugger Vs GDB!!!!!!!
@TheSulross11 ай бұрын
ok, this is really only something for self learning purposes - no utility toward production scenarios There are potentially some cheaper 'if test' that could determine if the address of the pointer refers to valid memory regions - such as the DATA segment and the heap area that has been mapped in. There is a garbage collecting memory manager that uses such techniques when tracing out what the still referenced memory allocations are.
@__hannibaal__ Жыл бұрын
Most beautiful thing in C and C++ is pointer, but i like it more unsafe, like i can read and write(????) on it at any where in RAM.
@cjreek8 ай бұрын
A late reply, but maybe this is still interesting or helpful to you: There are a couple "restrictions" to pointers: 1) The most important thing: "normal" (user space) programms can only access virtual memory (virtual address space). The operating system creates those "fake" address spaces for each process which makes it so that each process gets the illusion of having access to the whole available 32/64-Bit memory address space. The OS maps actual physical memory into the virtual address space of each process. But it only maps as much as the process needs. So the majority of the processes address space is not mapped to actual memory most of the time. And if you try to access an address in the virtual address space of the process that is not mapped to any actual physical memory you get the classic segfault. 2) Another effect of this virtual address space is that each process pretty much runs in a "memory sandbox", as there is no way to access memory of other processes, except if the operating system (or some kernel driver) offers syscalls that let you read from/write to memory of another process. In Linux there's the pseudo file /proc/$pid/mem that let's you do that or the ReadProcessMemory/WriteProcessMemory APIs in Windows. 3) What addresses you can read/write to also depends on the protection flags of the memory page that the address you're trying to access. Some memory for example is marked as readonly and you can't write to it with a pointer. But you can change the protection flags with mprotect (VirtualProtect for Windows) to make it writeable for example. Fun fact: Early operating systems like DOS did not have this virtual address space and you actually could just read/write foreign process memory without any restrictions. But that of course is also highly dangerous.
@tattis7 Жыл бұрын
How come you can use "symbol ≥ means greater than or equal to" sign in your code??? Isn't the valid operator ">="???
@JacobSorber Жыл бұрын
It's a font ligature. I've mentioned it in previous videos. Sorry it caused confusion.
@__hannibaal__ Жыл бұрын
And ……. I don’t get it yet…… check pointer at runtime for type own or … i will do as exercise and look what i found, Thanks.
@MrTrollland Жыл бұрын
this is ub. how can u be certain ub will result in segfault?
@jucelinocudecheque9607 Жыл бұрын
Could I not, at beggining of the main function, take the address of argc as the top of the stack and use sbrk(0) to get the bottom of the heap and check if the pointer is included in the range? EDIT: &argc is not exactly the top but it's close, and you wouldn't want to write there anyway EDIT: It's missing the .bss and .data segments
@maxaafbackname5562 Жыл бұрын
You also missed spaces mapped, direct or indirect, with mmap(). For example large blocks are directly mmap()'ed by mallic() in stead of allocated with the heap manager from the heap.
@JacobSorber Жыл бұрын
Yep, malloc a large block (50MB) and look at the address. On most modern OSes, it won't be where your small heap objects are.
@loc4725 Жыл бұрын
I wouldn't mind seeing memory protections.
@zxuiji Жыл бұрын
Eh, I still prefer the segfaults, at least they're easier to track down the source of the bug. That said I do see how this could be useful for servers that need to be able to keep themselves running even if they do encounter buggy pointers.
@xravenx24fe Жыл бұрын
You can have a bad pointer and still not segfault, and that's even worse because your program may seem to run properly, or crash at a totally different point.
@zxuiji Жыл бұрын
@@xravenx24fe true but in this context that's not relevent as the code only checks for unmapped addresses, which triggers a segfault always. The kind you're talking about won't ever be caught by simply trying to read/write to them
@tetraquark2402 Жыл бұрын
Ouch!
@synchro-dentally1965 Жыл бұрын
8:49 Can you think of a special case in which "waitpid()" would never return?
@rian0xFFF Жыл бұрын
If it is on child?
@_resh2265 Жыл бұрын
Simply if the child process happens to call `pause()` or any other system call that makes it halt for an undertemined amount of time, then `waitpid()` called by the parent would wait for its child to terminate, which might never happen. This is why you can pass the option WNOHANG (defined in wait.h) to waitpid, which changes waitpid's behaviour so that it just checks if the child has exited but doesn't actually "wait" as long as said child process is still running. Refer to `man 2 waitpid` for more info. EDIT: This happens when you start a shell session in an already running shell. The parent shell forks, then launches the second program (by calling execve or an alternative), and waits for it to terminate. So if the second the seconde program is another shell, it will show a prompt and read the standard input, which pauses it (for the user to actually type their input). So in that example, the "parent" shell is kind of stuck in its call to `waitpid`, because the "child" shell is busy operating its function, and does not terminate immediately. However, in the use case presented for waitpid in this video, it seems impossible for waitpid to never return.
@SlideRSB Жыл бұрын
Seems like this kind'a equates to child sacrifice.
@JacobSorber Жыл бұрын
Yes, it does.
@xravenx24fe Жыл бұрын
Theres no reason not to check pointers...i doubt that a handful of extra checks that might not even be built into the release build will be a bottleneck like...ever lol.
@sverkeren Жыл бұрын
There is almost never ever any reason to check pointers.