[systemd-devel] [udev] Wrong PID used in netlink socket
Sven Schnelle
svens at stackframe.org
Thu Oct 10 06:08:51 PDT 2013
Hi List,
i was debugging a problem in my own program which sometimes received
'Address already in use' during creation of the netlink socket. It
turned out
that udevd has the same bug. What actually happens is that udevd opens
the netlink socket, and forks afterwards. that doesn't sound bad at all, but
the pid of udevd is stored inside the nl_sockaddr structure. So after udevd
has forked, the PID stored in the kernel is no longer existent. If
another process
is now started that wants to do netlink communication with the kernel and
has (by coincidence) the same PID, it will fail.
Example from my system running udev:
pid of udevd is 18921:
# pidof udevd
18921
get the netlink socket for pid 18921:
$ lsof -np 18921
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
udevd 18921 root cwd DIR 253,1 4096 2 /
udevd 18921 root rtd DIR 253,1 4096 2 /
udevd 18921 root txt REG 253,1 161776 7823451 /sbin/udevd
udevd 18921 root mem REG 253,1 47080 2122850
/lib/i386-linux-gnu/i686/cmov/libnss_files-2.17.so
udevd 18921 root mem REG 253,1 42668 2122852
/lib/i386-linux-gnu/i686/cmov/libnss_nis-2.17.so
udevd 18921 root mem REG 253,1 13856 2122844
/lib/i386-linux-gnu/i686/cmov/libdl-2.17.so
udevd 18921 root mem REG 253,1 125258 2122837
/lib/i386-linux-gnu/i686/cmov/libpthread-2.17.so
udevd 18921 root mem REG 253,1 255908 688195
/lib/i386-linux-gnu/libpcre.so.3.13.1
udevd 18921 root mem REG 253,1 1759012 2122841
/lib/i386-linux-gnu/i686/cmov/libc-2.17.so
udevd 18921 root mem REG 253,1 30696 2122856
/lib/i386-linux-gnu/i686/cmov/librt-2.17.so
udevd 18921 root mem REG 253,1 133088 658519
/lib/i386-linux-gnu/libselinux.so.1
udevd 18921 root mem REG 253,1 87940 2122847
/lib/i386-linux-gnu/i686/cmov/libnsl-2.17.so
udevd 18921 root mem REG 253,1 30560 2122848
/lib/i386-linux-gnu/i686/cmov/libnss_compat-2.17.so
udevd 18921 root mem REG 253,1 134376 8478759
/lib/i386-linux-gnu/ld-2.17.so
udevd 18921 root 0u CHR 1,3 0t0 1029 /dev/null
udevd 18921 root 1u CHR 1,3 0t0 1029 /dev/null
udevd 18921 root 2u CHR 1,3 0t0 1029 /dev/null
udevd 18921 root 3u unix 0xc019b940 0t0 784351
/run/udev/control
udevd 18921 root 4u netlink 0t0 784352 KOBJECT_UEVENT
udevd 18921 root 5u REG 0,13 8 784354
/run/udev/queue.bin
udevd 18921 root 6r 0000 0,9 0 4048 anon_inode
udevd 18921 root 7u 0000 0,9 0 4048 anon_inode
udevd 18921 root 8u unix 0xdf574040 0t0 788161 socket
udevd 18921 root 9u unix 0xe3ddb4c0 0t0 788162 socket
udevd 18921 root 10u 0000 0,9 0 4048 anon_inode
udevd 18921 root 11u unix 0xe3ddb940 0t0 788165 socket
-> 784352
check PID with /proc/net/netlink:
$ grep 784352 /proc/net/netlink
e70ad800 15 18920 00000001 0 0 0 2 0 784352
tells 18920, which is the pid before the demonize fork.
I'm using the following diff (fork before opening the netlink socket):
$ git diff
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 7c6c5d6..4e0a789 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1003,6 +1003,7 @@ int main(int argc, char *argv[])
/* before opening new files, make sure std{in,out,err} fds are
in a sane state */
if (daemonize) {
int fd;
+ pid_t pid;
fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
@@ -1016,6 +1017,23 @@ int main(int argc, char *argv[])
fprintf(stderr, "cannot open /dev/null\n");
log_error("cannot open /dev/null\n");
}
+
+ pid = fork();
+ switch (pid) {
+ case 0:
+ break;
+ case -1:
+ log_error("fork of daemon failed: %m\n");
+ rc = 4;
+ goto exit;
+ default:
+ rc = EXIT_SUCCESS;
+ goto exit_daemonize;
+ }
+
+ setsid();
+
+ write_string_file("/proc/self/oom_score_adj", "-1000");
}
if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
@@ -1081,28 +1099,8 @@ int main(int argc, char *argv[])
goto exit;
}
- if (daemonize) {
- pid_t pid;
-
- pid = fork();
- switch (pid) {
- case 0:
- break;
- case -1:
- log_error("fork of daemon failed: %m\n");
- rc = 4;
- goto exit;
- default:
- rc = EXIT_SUCCESS;
- goto exit_daemonize;
- }
-
- setsid();
-
- write_string_file("/proc/self/oom_score_adj", "-1000");
- } else {
+ if (!daemonize)
sd_notify(1, "READY=1");
- }
print_kmsg("starting version " VERSION "\n");
Regards,
Sven
More information about the systemd-devel
mailing list