Path: csiph.com!usenet.pasdenom.info!news.albasani.net!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: Rainer Weikusat Newsgroups: comp.os.linux.development.apps,comp.unix.programmer Subject: Re: Linux O_NONBLOCK bug/ quirk Followup-To: comp.unix.programmer Date: Wed, 16 Apr 2014 12:42:55 +0100 Lines: 82 Message-ID: <877g6pv6tc.fsf@sable.mobileactivedefense.com> References: <878urvu0gx.fsf@sable.mobileactivedefense.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: individual.net hvRAn+MLYmK/y33wLEaDLwGsy8np7+YInMDQ9fluSLB0/0S0w= Cancel-Lock: sha1:Z9wpbuohp+TdhVH0f1qQBpVOqKA= sha1:Rx6CJrPkbln/Ov8OQJ9IyGvOryI= User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (gnu/linux) Xref: csiph.com comp.os.linux.development.apps:693 comp.unix.programmer:5303 Lusotec writes: [demonstration of Linux bug] > a fcntl must affect a already running operations. I think this > is very problematic. NB: This is crossposted to comp.unix.programmer with a followup-to set as it is (in my opinion) mostly about POSIX/ UNIX(*) implementation quirks. NB^2: This has been tested on Linux 3.2.9 without the recent "nonblocking fix" applied, I'm totally innocent of this mess. As someone named John McCue discovered, the fcntl does actually affect the running operation, although in rather strange ways: In case a non-blocking read which happened to be blocking is interrupted (this shouldn't happen because it shouldn't block aka 'go to sleep in the kernel until some external event occurs'), the kernel tries to hide that by silently returning an EAGAIN error instead of an EINTR error. But it doesn't keep track of which calls were blocking and which weren't. The net effect is that a blocking call supposed to be restarted in case it was interrupted by a signal actually aborts with EAGAIN since glibc "doesn't expect that" (apparently a lesser problem, given that nobody uses it ...). Sample program: --------------- #include #include #include #include #include #include #include static void dummy(int unused) { (void)unused; } int main(void) { struct sigaction sa; int sk, rc; pid_t pid; sk = socket(AF_UNIX, SOCK_DGRAM, 0); if (sk == -1) { perror("socket"); exit(1); } switch (pid = fork()) { case -1: perror("fork"); exit(1); case 0: sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = dummy; sigaction(SIGALRM, &sa, NULL); alarm(5); rc = read(sk, &pid, sizeof(pid)); if (rc == -1 && errno == EAGAIN) puts("Holy cow! How did this happen to me??"); _exit(0); } sleep(1); rc = fcntl(sk, F_GETFL); fcntl(sk, F_SETFL, rc | O_NONBLOCK); wait(NULL); return 0; }