Live data from Hacker News

fork() can fail

rachelbythebay.com

231–240 of 320 posts

Re: fork() can fail

#231

Earlier quoted context omitted.

What's wrong with the C version? char *dir = "/foo"; mkdir(dir, 0700); if (chdir(dir) == 0) delete_all_files();

That's incomplete in that it doesn't automatically chdir back. A proper block-scope "with-" macro will wrap the body in something like: char *olddir = getcwd(); chdir(newdir); try { do_stuff(); } finally { chdir(olddir); }

Any multithreaded code will be destroyed if relying on chdir. chdir should be deprecated, as resolving relative to is just trivial in an application.

Re: fork() can fail

#232

Earlier quoted context omitted.

That's incomplete in that it doesn't automatically chdir back. A proper block-scope "with-" macro will wrap the body in something like: char *olddir = getcwd(); chdir(newdir); try { do_stuff(); } finally { chdir(olddir); }

'try' and 'finally' are in C now? Someone should warn the GCC guys they're behind the times. Also, getcwd has a size parameter these days, and of course you want to check if the getcwd actually worked.

The OP said: wrap in a macro having similar properties (ala pseudo code).

Re: fork() can fail

#233
post #213
post #209

When I was young and really didn't understand Unix, my friend and were summer students at NBS (now NIST), and one fine afternoon we wondered what would happen if you ran fork() forever. We didn't know, so we wrote the program and ran it. This was on a PDP-11/45 running v6 or v7 Unix. The printing console (some DECWriter 133 something or other) started burping and spewing stuff about fork failing and other bad things,…

> ...a minute or two later one of the folks who had 'root' ran into the machine room with a panic-stricken look because the system had mostly just locked up. It's kind of weird that, while root has always had e.g. 5% reserved disk space on the rootfs for emergencies, one thing no Unix has ever done is enforce a 5% CPU reservation for root so administrators can "talk over" a cascading failure. I think this is possible…

If you care about such things the normal method is to have a backup ssh running on a different port with realtime priority , it is not used at any other time except when some process had gone runaway and you can't do anything else.

Re: fork() can fail

#234
post #228

Earlier quoted context omitted.

Indeed. I like using a VERIFY macro: #ifdef NDEBUG # define VERIFY(x) ((x), 1) #else # define VERIFY(x) assert((x)) #endif Then you can write VERIFY(pthread_mutex_unlock(&lock) == 0); You don't need, however, to consider the possibility of your program continuing to run after pthread_mutex_unlock fails.

Huh? You do realize that the standard assert() macro already is compiled out if NDEBUG is defined, right? Your code could just as well be written as assert(pthread_mutex_unlock(&lock) == 0); which of course has the added benefit of not inventing anything new, i.e. being standard and immediately understood by anyone who knows the language and its libraries reasonably well.

Be very careful with that. When compiled under NDEBUG and the assert gone, the mutex unlock will be gone too and you'll wonder why the application stops working.

Re: fork() can fail

#235
post #228

Earlier quoted context omitted.

Indeed. I like using a VERIFY macro: #ifdef NDEBUG # define VERIFY(x) ((x), 1) #else # define VERIFY(x) assert((x)) #endif Then you can write VERIFY(pthread_mutex_unlock(&lock) == 0); You don't need, however, to consider the possibility of your program continuing to run after pthread_mutex_unlock fails.

Huh? You do realize that the standard assert() macro already is compiled out if NDEBUG is defined, right? Your code could just as well be written as assert(pthread_mutex_unlock(&lock) == 0); which of course has the added benefit of not inventing anything new, i.e. being standard and immediately understood by anyone who knows the language and its libraries reasonably well.

As per jacquesm's comment below[0], your example would, by default, be compiled out if NDEBUG is defined. So in production you'd never release the lock.

[0] https://news.ycombinator.com/item?id=8206258

Re: fork() can fail

#236
post #213
post #209

When I was young and really didn't understand Unix, my friend and were summer students at NBS (now NIST), and one fine afternoon we wondered what would happen if you ran fork() forever. We didn't know, so we wrote the program and ran it. This was on a PDP-11/45 running v6 or v7 Unix. The printing console (some DECWriter 133 something or other) started burping and spewing stuff about fork failing and other bad things,…

> ...a minute or two later one of the folks who had 'root' ran into the machine room with a panic-stricken look because the system had mostly just locked up. It's kind of weird that, while root has always had e.g. 5% reserved disk space on the rootfs for emergencies, one thing no Unix has ever done is enforce a 5% CPU reservation for root so administrators can "talk over" a cascading failure. I think this is possible…

Most linux distributions assign root processes a better scheduling priority than non-root processes, which should be good enough in most cases. Critical system processes also run at better priorities than other processes. It's not uncommon to see linux users consciously decide on the priority of a process by using nice or renice.

Totally limiting the CPU utilization of a group of processes requires more overhead than changing the scheduling priority since you must actively account for the CPU usage. CPU cgroups should do just that though and in most cases the overhead should be acceptable.

In your comment's parent, I don't think raw CPU utilization was the issue since kabdib mentioned fork and it was in response to a post about fork failures. The problems caused by a fork bomb are not limited to CPU utilization, see: https://en.wikipedia.org/wiki/Fork_bomb

In any case, there will likely always be some system call you can abuse to totally exhaust some resource of the kernel.

Re: fork() can fail

#237
post #228

Earlier quoted context omitted.

Indeed. I like using a VERIFY macro: #ifdef NDEBUG # define VERIFY(x) ((x), 1) #else # define VERIFY(x) assert((x)) #endif Then you can write VERIFY(pthread_mutex_unlock(&lock) == 0); You don't need, however, to consider the possibility of your program continuing to run after pthread_mutex_unlock fails.

Huh? You do realize that the standard assert() macro already is compiled out if NDEBUG is defined, right? Your code could just as well be written as assert(pthread_mutex_unlock(&lock) == 0); which of course has the added benefit of not inventing anything new, i.e. being standard and immediately understood by anyone who knows the language and its libraries reasonably well.

You're misreading the code. When NDEBUG is defined, we don't use assert, but instead always evaluate the expression. The overall effect is that we always evaluate VERIFY's argument, but only check it in debug builds.

Re: fork() can fail

#238
post #73

And this right there is why exceptions are a superior mechanism of announcing errors...

Multiple return would be fine too. pid,err = fork()

You want exclusive cases between error and validity, MRV is absolutely not good for that.

Erlang gets away with it because dynamically typed + pattern matching + uses MRV for tagged returns, the second property makes it work.

Re: fork() can fail

#239
post #139

Earlier quoted context omitted.

Are you sure? And what if $programmer forgets to check what's in err? What would pid contain in that case? I mention this because I guess you quoted a kind of syntax that matches the one from Go. So then I'm guessing that Go would simply ignore the error in this case. However, having a proper exception mechanism, if you don't catch the problem, then it bubbles up, and the program doesn't continue with wrong data (whi…

Yeah, better than returning a tuple, it should return a sum type. Then you would need to deconstruct it, like how people suggest using a switch: match fork() -> | Error(errno) -> ... | Pid(pid) -> ... Or a general "Choice" sum, perhaps using phantom types so int isn't compatible with int . But then all of a sudden, instead of a single word being returned, a tag and possibly variably-sized result has to be returned, a…

A "Choice" (Either in Haskell, Result in Rust) wouldn't work for fork() as it can have 3 results, and you'd want the `Child` case cleanly and easily separated from `Pid`.

Re: fork() can fail

#240
post #131

Earlier quoted context omitted.

I could argue, however, that in this particular case, it's the user's fault for failing to understand the full and defined behavior of fork() in addition to failing to understand the full and defined behavior of other functions, ...say, kill(). It's just as wrong to feed kill() -1 as it would be to feed it -48585 or "babdkd" (unless that is explicitly your intention). A simple sanity check of if [ "${pid} > "0" ]; is…

Fault is not a rivalrous good. It's the user's fault, and it's the API creator's fault. Is there a reason fork can't be changed to just crash the program on failure? Are situations where a program usefully does something other than crash on fork failure, more or less common than situations where a program fails in the way described in the article?

I can't think of any good cases for crashing a program when fork fails. If you're doing work in a pool of processes and the parent process tries to fork another process, but fails, there are many ways to handle this: wait a few seconds before trying again, wait for another process to finish, kill a few processes, etc.

If you were forking in a high-level language such as Python, failing to fork would raise an exception which would possibly crash the program if left unhandled.

C does not have exceptions so return codes are used to indicate success or failure. This is true for nearly every function, not just fork. If you're not checking for errors in a C program, it's going to break in unexpected ways, and will possibly be vulnerable to exploitation.

fork has 3 possible return values: - 0 for the child process - a positive number for the parent process - a negative number if it failed.

If you look at the man page's "Return Value" section, it is extremely clear, see: http://linux.die.net/man/2/fork

"On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately."

Post reply on HN