Discussion:
fork error using kevent rather than wait(2)
Luke Small
2017-06-16 08:16:19 UTC
Permalink
I made a simple case. I need to have a kevent wait for both incoming socket
communications and for the forked process to finish its run evaluating
shared data.
I show that it fails to merely wait for NOTE_EXIT calls and I'd guess
making the kernel aware that the process died.

limit_test.c shows that there is a fork error when it runs a while even if
it waits for the forked process to die, where it doesn't occur by simply
using wait(2), which is in comments below it.
Otto Moerbeek
2017-06-16 14:20:13 UTC
Permalink
Post by Luke Small
I made a simple case. I need to have a kevent wait for both incoming socket
communications and for the forked process to finish its run evaluating
shared data.
I show that it fails to merely wait for NOTE_EXIT calls and I'd guess
making the kernel aware that the process died.
limit_test.c shows that there is a fork error when it runs a while even if
it waits for the forked process to die, where it doesn't occur by simply
using wait(2), which is in comments below it.
I do not see a fork error, but a kevent error:

...
24
25
a.out: kevent: No such process

You code indeed has a race condition: the child proces might already
have exited the moment you call kevent.

wait(2) is a bit different: it allows for waiting on a proces that already
has died but hasn't been waited for yet. The child process will have the
zombie state in the meantime.

-Otto
Post by Luke Small
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/wait.h>
int main()
{
int i;
int k;
int kq;
int integer;
struct kevent ke;
kq = kqueue();
if (kq == -1)
err(1, "kq!");
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
EV_SET(&ke, integer, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, NULL);
if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
k = kevent(kq, NULL, 0, &ke, 1, NULL);
if (k == -1)
err(1, "k == -1");
if(ke.flags & EV_ERROR)
err(1, "EV_ERROR");
printf("%d\n", i);
}
}
/*
int main()
{
int i;
int integer;
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
wait(NULL);
printf("%d\n", i);
}
}
*/
Luke Small
2017-06-16 14:36:03 UTC
Permalink
I had it where it made a pipe and the child waited on a recv() before the
parent registered the kevent and closed the pipe. On my virtual machine,
this code works the same. It spawns a fork() error and my large project
makes it where no roxterm can spawn a new ksh instance.
Post by Luke Small
Post by Luke Small
I made a simple case. I need to have a kevent wait for both incoming
socket
Post by Luke Small
communications and for the forked process to finish its run evaluating
shared data.
I show that it fails to merely wait for NOTE_EXIT calls and I'd guess
making the kernel aware that the process died.
limit_test.c shows that there is a fork error when it runs a while even
if
Post by Luke Small
it waits for the forked process to die, where it doesn't occur by simply
using wait(2), which is in comments below it.
...
24
25
a.out: kevent: No such process
You code indeed has a race condition: the child proces might already
have exited the moment you call kevent.
wait(2) is a bit different: it allows for waiting on a proces that already
has died but hasn't been waited for yet. The child process will have the
zombie state in the meantime.
-Otto
Post by Luke Small
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/wait.h>
int main()
{
int i;
int k;
int kq;
int integer;
struct kevent ke;
kq = kqueue();
if (kq == -1)
err(1, "kq!");
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
EV_SET(&ke, integer, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT,
0, NULL);
Post by Luke Small
if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
k = kevent(kq, NULL, 0, &ke, 1, NULL);
if (k == -1)
err(1, "k == -1");
if(ke.flags & EV_ERROR)
err(1, "EV_ERROR");
printf("%d\n", i);
}
}
/*
int main()
{
int i;
int integer;
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
wait(NULL);
printf("%d\n", i);
}
}
*/
Otto Moerbeek
2017-06-16 14:44:45 UTC
Permalink
Post by Luke Small
I had it where it made a pipe and the child waited on a recv() before the
parent registered the kevent and closed the pipe. On my virtual machine,
this code works the same. It spawns a fork() error and my large project
makes it where no roxterm can spawn a new ksh instance.
You might have created a fork bomb with other code, but that is is not
what your example code is doing.

Note that the bugs@ mailing list is for bug reporing. In your case it
does sound more like you need to coding help.

-Otto
Post by Luke Small
Post by Luke Small
Post by Luke Small
I made a simple case. I need to have a kevent wait for both incoming
socket
Post by Luke Small
communications and for the forked process to finish its run evaluating
shared data.
I show that it fails to merely wait for NOTE_EXIT calls and I'd guess
making the kernel aware that the process died.
limit_test.c shows that there is a fork error when it runs a while even
if
Post by Luke Small
it waits for the forked process to die, where it doesn't occur by simply
using wait(2), which is in comments below it.
...
24
25
a.out: kevent: No such process
You code indeed has a race condition: the child proces might already
have exited the moment you call kevent.
wait(2) is a bit different: it allows for waiting on a proces that already
has died but hasn't been waited for yet. The child process will have the
zombie state in the meantime.
-Otto
Post by Luke Small
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/wait.h>
int main()
{
int i;
int k;
int kq;
int integer;
struct kevent ke;
kq = kqueue();
if (kq == -1)
err(1, "kq!");
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
EV_SET(&ke, integer, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT,
0, NULL);
Post by Luke Small
if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
k = kevent(kq, NULL, 0, &ke, 1, NULL);
if (k == -1)
err(1, "k == -1");
if(ke.flags & EV_ERROR)
err(1, "EV_ERROR");
printf("%d\n", i);
}
}
/*
int main()
{
int i;
int integer;
for (i = 0; i < 2000; ++i)
{
integer = fork();
switch(integer)
{
err(1, "fork!");
_exit(0);
break;
}
wait(NULL);
printf("%d\n", i);
}
}
*/
Luke Small
2017-06-16 14:50:34 UTC
Permalink
This is what I'm talking about.
Todd C. Miller
2017-06-16 15:09:21 UTC
Permalink
You cannot use recv on a pipe, it will fail with ENOTSOCK.
Use read/write or try socketpair() instead.

- todd
Luke Small
2017-06-16 15:26:12 UTC
Permalink
Thanks! I was hesitant to use recv, since I use it in socketpairs in my
project.
Post by Todd C. Miller
You cannot use recv on a pipe, it will fail with ENOTSOCK.
Use read/write or try socketpair() instead.
- todd
Luke Small
2017-06-17 08:44:30 UTC
Permalink
It works if you put a waitpid() in there, but if that is true, what's the
point of having ke.data returning the status when the wait() series will do
it?
Post by Luke Small
Thanks! I was hesitant to use recv, since I use it in socketpairs in my
project.
Post by Todd C. Miller
You cannot use recv on a pipe, it will fail with ENOTSOCK.
Use read/write or try socketpair() instead.
- todd
Philip Guenther
2017-06-18 07:01:46 UTC
Permalink
Post by Luke Small
It works if you put a waitpid() in there, but if that is true, what's the
point of having ke.data returning the status when the wait() series will do
it?
You can use kevent() to see the exit status of a process that you cannot
waitpid() for.


Note that kevent() EVFILT_PROC filters don't reap zombies, so your program
is accumulating zombie children until you reach your process limit.

As for the behavior of EVFILT_PROC on an already exited process, I look
forward to your sendbug report.


Philip Guenther
Luke Small
2017-06-18 07:08:44 UTC
Permalink
I think that the fork mechanism is slower on the virtualbox virtual machine
because EVFILT_PROC detects the child.
Post by Philip Guenther
Post by Luke Small
It works if you put a waitpid() in there, but if that is true, what's the
point of having ke.data returning the status when the wait() series will
do
Post by Luke Small
it?
You can use kevent() to see the exit status of a process that you cannot
waitpid() for.
Note that kevent() EVFILT_PROC filters don't reap zombies, so your program
is accumulating zombie children until you reach your process limit.
As for the behavior of EVFILT_PROC on an already exited process, I look
forward to your sendbug report.
Philip Guenther
Luke Small
2017-06-18 07:59:00 UTC
Permalink
I did a sendbug. maybe it didn't go through. this is my dmesg if it didn't.
It does seem odd that a quickly exiting process would be detected. It seems
like I've had problems with EVFILT_PROC failing because of a quickly
exiting process in previous versions (which I know isn't a bug). This is
the latest stable version of 6.1 It's running on virtualbox on windows 7.

OpenBSD 6.1-stable (GENERIC.MP) #0: Thu Jun 15 09:01:11 CDT 2017
***@puffy64qemu.my.domain:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 5414780928 (5163MB)
avail mem = 5245997056 (5002MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.5 @ 0xe1000 (10 entries)
bios0: vendor innotek GmbH version "VirtualBox" date 12/01/2006
bios0: innotek GmbH VirtualBox
acpi0 at bios0: rev 2
acpi0: sleep states S0 S5
acpi0: tables DSDT FACP APIC SSDT
acpi0: wakeup devices
acpitimer0 at acpi0: 3579545 Hz, 32 bits
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1616.70 MHz
cpu0:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: TSC frequency 1616704640 Hz
cpu0: smt 0, core 0, package 0
mtrr: CPU supports MTRRs but not enabled by BIOS
cpu0: apic clock running at 999MHz
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1616.71 MHz
cpu1:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 0, core 1, package 0
cpu2 at mainbus0: apid 2 (application processor)
cpu2: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1616.85 MHz
cpu2:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 0, core 2, package 0
cpu3 at mainbus0: apid 3 (application processor)
cpu3: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1617.11 MHz
cpu3:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 0, core 3, package 0
cpu4 at mainbus0: apid 4 (application processor)
cpu4: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1620.36 MHz
cpu4:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu4: 256KB 64b/line 8-way L2 cache
cpu4: smt 0, core 4, package 0
cpu5 at mainbus0: apid 5 (application processor)
cpu5: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1624.31 MHz
cpu5:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu5: 256KB 64b/line 8-way L2 cache
cpu5: smt 0, core 5, package 0
cpu6 at mainbus0: apid 6 (application processor)
cpu6: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1638.13 MHz
cpu6:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu6: 256KB 64b/line 8-way L2 cache
cpu6: smt 0, core 6, package 0
cpu7 at mainbus0: apid 7 (application processor)
cpu7: Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz, 1639.28 MHz
cpu7:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,HTT,SSE3,SSSE3,SSE4.1,SSE4.2,NXE,RDTSCP,LONG,LAHF,ITSC
cpu7: 256KB 64b/line 8-way L2 cache
cpu7: smt 0, core 7, package 0
ioapic0 at mainbus0: apid 8 pa 0xfec00000, version 20, 24 pins
acpiprt0 at acpi0: bus 0 (PCI0)
acpicpu0 at acpi0: C1(@1 halt!)
acpicpu1 at acpi0: C1(@1 halt!)
acpicpu2 at acpi0: C1(@1 halt!)
acpicpu3 at acpi0: C1(@1 halt!)
acpicpu4 at acpi0: C1(@1 halt!)
acpicpu5 at acpi0: C1(@1 halt!)
acpicpu6 at acpi0: C1(@1 halt!)
acpicpu7 at acpi0: C1(@1 halt!)
"PNP0303" at acpi0 not configured
"PNP0F03" at acpi0 not configured
acpibat0 at acpi0: BAT0 model "1" serial 0 type VBOX oem "innotek"
acpiac0 at acpi0: AC unit online
acpivideo0 at acpi0: GFX0
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel 82441FX" rev 0x02
pcib0 at pci0 dev 1 function 0 "Intel 82371SB ISA" rev 0x00
pciide0 at pci0 dev 1 function 1 "Intel 82371AB IDE" rev 0x01: DMA, channel
0 configured to compatibility, channel 1 configured to compatibility
wd0 at pciide0 channel 0 drive 0: <VBOX HARDDISK>
wd0: 128-sector PIO, LBA, 51200MB, 104857600 sectors
wd0(pciide0:0:0): using PIO mode 4, Ultra-DMA mode 2
atapiscsi0 at pciide0 channel 1 drive 0
scsibus1 at atapiscsi0: 2 targets
cd0 at scsibus1 targ 0 lun 0: <VBOX, CD-ROM, 1.0> ATAPI 5/cdrom removable
cd0(pciide0:1:0): using PIO mode 4, Ultra-DMA mode 2
vga1 at pci0 dev 2 function 0 "InnoTek VirtualBox Graphics Adapter" rev 0x00
wsdisplay0 at vga1 mux 1: console (80x25, vt100 emulation)
wsdisplay0: screen 1-5 added (80x25, vt100 emulation)
virtio0 at pci0 dev 3 function 0 "Qumranet Virtio Network" rev 0x00
vio0 at virtio0: address 08:00:27:b8:cd:57
virtio0: apic 8 int 19
"InnoTek VirtualBox Guest Service" rev 0x00 at pci0 dev 4 function 0 not
configured
auich0 at pci0 dev 5 function 0 "Intel 82801AA AC97" rev 0x01: apic 8 int
21, ICH
ac97: codec id 0x83847600 (SigmaTel STAC9700)
audio0 at auich0
ohci0 at pci0 dev 6 function 0 "Apple Intrepid USB" rev 0x00: apic 8 int
22, version 1.0
piixpm0 at pci0 dev 7 function 0 "Intel 82371AB Power" rev 0x08: apic 8 int
23
iic0 at piixpm0
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
usb0 at ohci0: USB revision 1.0
uhub0 at usb0 configuration 1 interface 0 "Apple OHCI root hub" rev
1.00/1.00 addr 1
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on wd0a (aac4bf2c287cc52a.a) swap on wd0b dump on wd0b

Theo de Raadt
2017-06-16 14:54:15 UTC
Permalink
Post by Luke Small
This is what I'm talking about.
Are you reporting an OpenBSD bug?

If you are please use sendbug and include ALL THE INFORMATION.

If you aren't reporting something which is clearly an OpenBSD bug,
go ask for help elsewhere.
Luke Small
2017-06-16 15:00:45 UTC
Permalink
How is this not a bug? Is this how fork() and NOTE_EXIT kevents are
supposed to behave? If it is supposed to behave this way, why does fork()
and wait() not behave this way?
Post by Theo de Raadt
Post by Luke Small
This is what I'm talking about.
Are you reporting an OpenBSD bug?
If you are please use sendbug and include ALL THE INFORMATION.
If you aren't reporting something which is clearly an OpenBSD bug,
go ask for help elsewhere.
Theo de Raadt
2017-06-16 15:02:18 UTC
Permalink
Why are you not using sendbug?

Are you really so special and the rules don't apply to you?
Post by Luke Small
How is this not a bug? Is this how fork() and NOTE_EXIT kevents are
supposed to behave? If it is supposed to behave this way, why does fork()
and wait() not behave this way?
Loading...