Another way to check pointers at runtime in C

Поделиться
HTML-код
  • Опубликовано: 18 сен 2023
  • Patreon ➤ / jacobsorber
    Courses ➤ jacobsorber.thinkific.com
    Website ➤ www.jacobsorber.com
    ---
    Another way to check pointers at runtime in C // We're talking about pointer checking again, mostly as an exploration of fork and processes, but also because it's just interesting and something we don't often do. I hope it's enlightening.
    Related Videos:
    Previous pointer checking video: • How to Check Your Poin...
    Fork video: • Creating new processes...
    ***
    Welcome! I post videos that help you learn to program and become a more confident software developer. I cover beginner-to-advanced systems topics ranging from network programming, threads, processes, operating systems, embedded systems and others. My goal is to help you get under-the-hood and better understand how computers work and how you can use them to become stronger students and more capable professional developers.
    About me: I'm a computer scientist, electrical engineer, researcher, and teacher. I specialize in embedded systems, mobile computing, sensor networks, and the Internet of Things. I teach systems and networking courses at Clemson University, where I also lead the PERSIST research lab.
    More about me and what I do:
    www.jacobsorber.com
    people.cs.clemson.edu/~jsorber/
    persist.cs.clemson.edu/
    To Support the Channel:
    + like, subscribe, spread the word
    + contribute via Patreon --- [ / jacobsorber ]
    Source code is also available to Patreon supporters. --- [jsorber-youtube-source.heroku...]

Комментарии • 50

  • @lolmanthecat
    @lolmanthecat 8 месяцев назад +34

    "you try it out on the child and if it dies you will know not to do it on the parent." spectacular.

  • @captainpingou930
    @captainpingou930 8 месяцев назад +6

    I like the way the safest way to check a ptr is to sacrificing a child 😂

  • @rian0xFFF
    @rian0xFFF 8 месяцев назад +2

    i'm glad you're back

    • @JacobSorber
      @JacobSorber  8 месяцев назад +2

      Thanks. Life's been super busy, but hopefully settling down a bit soon.

  • @DemonSlayer627
    @DemonSlayer627 8 месяцев назад +7

    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
      @01rnr01 8 месяцев назад

      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?

  • @miteshsharma3106
    @miteshsharma3106 8 месяцев назад +3

    Would love to see the video about creating pointers with specific privileges (read only, write only, etc)

  • @unperrier5998
    @unperrier5998 8 месяцев назад +3

    Thanks Jacob. Maybe next you could do a video using mmap() and/or madvise() to do the same thing.

  • @SimonJentzschX7
    @SimonJentzschX7 8 месяцев назад +2

    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?

  • @johnpawlicki1184
    @johnpawlicki1184 8 месяцев назад +1

    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.

  • @paherbst524
    @paherbst524 8 месяцев назад

    This is clever. I like it.

  • @AK-vx4dy
    @AK-vx4dy 8 месяцев назад +1

    Neat trick

  • @beezball38
    @beezball38 7 месяцев назад +1

    would be a bit intensive to keep copying the entire address space for larger programs, still neat

  • @questionmarc8
    @questionmarc8 Месяц назад

    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.

  • @alejandropoirier6493
    @alejandropoirier6493 8 месяцев назад

    Love this channel, thanks for such quality videos!

  • @Xiterlide
    @Xiterlide 8 месяцев назад +2

    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
      @CodeActice 8 месяцев назад

      You would be sued for putting your child's life in danger in the Kernel Court!

  • @cjreek
    @cjreek 2 месяца назад

    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

  • @Bruno_Haible
    @Bruno_Haible 8 месяцев назад +3

    Two other approaches are: Read the /proc/self/maps file, which contains the ranges of valid addresses. Or use the mincore() system call.

  • @andrewporter1868
    @andrewporter1868 8 месяцев назад

    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).

  • @danielrhouck
    @danielrhouck 8 месяцев назад +2

    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
      @maxaafbackname5562 8 месяцев назад

      Pointers to hardware are differently mapped than pointers in the process space.

    • @danielrhouck
      @danielrhouck 8 месяцев назад

      @@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
      @maxaafbackname5562 8 месяцев назад

      @@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
      @maxaafbackname5562 8 месяцев назад

      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
      @danielrhouck 8 месяцев назад

      @@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.

  • @TheSulross
    @TheSulross 6 месяцев назад

    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.

  • @jucelinocudecheque9607
    @jucelinocudecheque9607 8 месяцев назад

    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
      @maxaafbackname5562 8 месяцев назад

      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
      @JacobSorber  8 месяцев назад

      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.

  • @tattis7
    @tattis7 7 месяцев назад

    How come you can use "symbol ≥ means greater than or equal to" sign in your code??? Isn't the valid operator ">="???

    • @JacobSorber
      @JacobSorber  7 месяцев назад +1

      It's a font ligature. I've mentioned it in previous videos. Sorry it caused confusion.

  • @abrarmasumabir3809
    @abrarmasumabir3809 8 месяцев назад +15

    Sir plz make a video on msvc debugger Vs GDB!!!!!!!

  • @__hannibaal__
    @__hannibaal__ 8 месяцев назад

    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.

    • @cjreek
      @cjreek 2 месяца назад

      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.

  • @__hannibaal__
    @__hannibaal__ 8 месяцев назад

    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.

  • @zxuiji
    @zxuiji 8 месяцев назад

    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
      @xravenx24fe 8 месяцев назад +3

      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
      @zxuiji 8 месяцев назад +1

      @@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
    @tetraquark2402 8 месяцев назад

    Ouch!

  • @MrTrollland
    @MrTrollland 8 месяцев назад

    this is ub. how can u be certain ub will result in segfault?

  • @loc4725
    @loc4725 8 месяцев назад +1

    I wouldn't mind seeing memory protections.

  • @synchro-dentally1965
    @synchro-dentally1965 8 месяцев назад

    8:49 Can you think of a special case in which "waitpid()" would never return?

    • @rian0xFFF
      @rian0xFFF 8 месяцев назад

      If it is on child?

    • @_resh2265
      @_resh2265 8 месяцев назад

      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
    @SlideRSB 8 месяцев назад

    Seems like this kind'a equates to child sacrifice.

  • @xravenx24fe
    @xravenx24fe 8 месяцев назад

    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
      @sverkeren 8 месяцев назад +2

      There is almost never ever any reason to check pointers.