"Syntax error" in default bindresvport.blacklist

Bug #306007 reported by AlainKnaff on 2008-12-07
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
glibc (Debian)
Fix Released
Unknown
glibc (Ubuntu)
Undecided
Unassigned
Nominated for Dapper by Jared Showalter
Nominated for Hardy by Jared Showalter
Nominated for Lucid by Jared Showalter
Nominated for Maverick by Jared Showalter

Bug Description

The default /etc/bindresvport.blacklist as shipped with Kubuntu cotains its entries (numbers) followed by a tab, and then a comment.
Apparently, the TAB causes the entries to be ignored.
We had one occurrence "in the wild" where rpc.quotad was hogging dovecot's 993 port, preventing dovecot to start up, although 993 is listed in /etc/bindresvport.blacklist.

In order to confirm that the behavior was indeed due to the TAB, I filled up /etc/bindresvport.blacklist for every port number from 512 til 1000, and restarted one of the services using bindresvport (I tested with the rpc.statd included in nfs-common), and it got a port below 1000.
After replacing the TABs with 5 spaces, it correctly got a port between 1001 and 1023.

I'm not really sure whether this is a bug in the default file (should have spaces) or in the parser (should consider all "whitespace" characters, including tab).

Colin Watson (cjwatson) wrote :

This is a bit mystifying, because as far as I can see the parser *does* honour tabs as you would expect. It just uses strtoul() on the whole line and thus stops at the first character that isn't part of a number.

'ltrace -S' might help to narrow down what's going on here.

Changed in glibc:
status: New → Incomplete
AlainKnaff (kubuntu-misc) wrote :
Download full text (3.4 KiB)

Unfortunately not :-(

root@hitchhiker:/home/alain/cprog# ltrace -S ~alain/bin/x86_64/bindresvport
SYS_brk(NULL) = 0x601000
SYS_mmap(0, 4096, 3, 34, 0xffffffff) = 0x7f0d60930000
SYS_access("/etc/ld.so.nohwcap", 00) = -2
SYS_mmap(0, 8192, 3, 34, 0xffffffff) = 0x7f0d6092e000
SYS_access("/etc/ld.so.preload", 04) = -2
SYS_open("/etc/ld.so.cache", 0, 01) = 3
SYS_fstat(3, 0x7fff6892ec20) = 0
SYS_mmap(0, 126618, 1, 2, 3) = 0x7f0d6090f000
SYS_close(3) = 0
SYS_access("/etc/ld.so.nohwcap", 00) = -2
SYS_open("/lib/libc.so.6", 0, 014044600000) = 3
SYS_read(3, "\177ELF\002\001\001", 832) = 832
SYS_fstat(3, 0x7fff6892ec70) = 0
SYS_mmap(0, 0x361278, 5, 2050, 3) = 0x7f0d603b2000
SYS_mprotect(0x7f0d6050a000, 2097152, 0) = 0
SYS_mmap(0x7f0d6070a000, 20480, 3, 2066, 3) = 0x7f0d6070a000
SYS_mmap(0x7f0d6070f000, 17016, 3, 50, 0xffffffff) = 0x7f0d6070f000
SYS_close(3) = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff) = 0x7f0d6090e000
SYS_mmap(0, 4096, 3, 34, 0xffffffff) = 0x7f0d6090d000
SYS_arch_prctl(4098, 0x7f0d6090d6e0, 0x7f0d6090d6e0, 34, 0) = 0
SYS_mprotect(0x7f0d6070a000, 12288, 1) = 0
SYS_munmap(0x7f0d6090f000, 126618) = 0
__libc_start_main(0x400538, 1, 0x7fff6892f888, 0x4005c0, 0x4005b0 <unfinished ...>
socket(2, 1, 0 <unfinished ...>
SYS_socket(2, 1, 0, 0, 0x7f0d6070f2e0) = 3
<... socket resumed> ) = 3
bindresvport(3, 0, 0, -1, 0x7f0d6070f2e0 <unfinished ...>
SYS_brk(NULL) = 0x601000
SYS_brk(0x622000) = 0x622000
SYS_open("/etc/bindresvport.blacklist", 0, 0666) = 4
SYS_fstat(4, 0x7fff6892f5b0) = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff) = 0x7f0d6092d000
SYS_read(4, "#\n# This file contains a list of"..., 4096) = 4096
SYS_read(4, "735 # zoz735\n736 # zoz73"..., 4096) = 4096
SYS_read(4, "\n978 # zoz978\n979 # zoz9"..., 4096) = 341
SYS_read(4, "", 4096) = 0
SYS_close(4) = 0
SYS_munmap(0x7f0d6092d000, 4096) = 0
SYS_getpid() = 11756
SYS_bind(3, 0x7fff6892f700, 16, 0, 0xffffffff) = 0
<... bindresvport resumed> ) = 0
SYS_exit_group(0 <no return ...>
+++ exited (status 0) +++

So, it really looks like it is only showing the top-level library calls (bindresvport) and syscalls (open, read...

Read more...

AlainKnaff (kubuntu-misc) wrote :

Just did an apt-get source glibc, and in the diff, I found the following code snippet, in glibc-2.7/debian/patches/any/local-bindresvport_blacklist.diff , in sunrpc/bindrsvprt.c , which seems to be meant to parse one line:

++ tmp = strchr (cp, '#'); /* remove comments */
++ if (tmp)
++ *tmp = '\0';
++ while (isspace ((int)*cp)) /* remove spaces and tabs */
++ ++cp;
++ if (*cp == '\0') /* ignore empty lines */
++ continue;
++ if (cp[strlen (cp) - 1] == '\n')
++ cp[strlen (cp) - 1] = '\0';
++
++ port = strtoul (cp, &tmp, 0);
++ if (*tmp != '\0' || (port == ULONG_MAX && errno == ERANGE))
++ continue;

It seems to:
1. Remove comments
2. Remove _leading_ spaces
3. Ignore empty lines
4. Remove final newline
5. Parse the number
6. ... but then _check_ whether the line finishes right after the number, by comparing the tmp pointer "returned" by strtoul with '\0'.

The point 6 has to go. Or if we want to keep some sanity checking, maybe there should be a "while (isspace ((int)*tmp)) ++tmp; /* remove spaces and tabs */" line between the strtoul and the check for *tmp != '\0'

AlainKnaff (kubuntu-misc) wrote :

Just out of curiosity: why is this marked as incomplete? What is missing to make it complete?

AlainKnaff's comment #3 is entirely correct. This bug is also easy to reproduce, as described in the initial comment, although as noted in comment #3, it is not tabs per se that are the problem: anything other than '#' or end-of-line is a problem.

I encountered this with ypbind taking the port 631 of cups, causing printing in GNOME to be disabled.

Most casual users will not report this bug because it will "go away on its own" if they reboot or something--but that is not the attitude to encourage.

Just to be completely specific about how to reproduce this (this was with 8.04, but I don't have any reason to believe it's been fixed):

0. Backup the file:
$ sudo mv /etc/bindresvport.blacklist /etc/bindresvport.blacklist.bak

1. Reserve all ports 600-999, with the format used for shipped bindresvport.blacklist (e.g., "631\t# cups")
$ perl -e 'for ($i=600; $i<1000; $i++) {print "$i\t# foo\n";}' | sudo dd of=/etc/bindresvport.blacklist
7+1 records in
7+1 records out
4011 bytes (4.0 kB) copied, 6.5962e-05 s, 60.8 MB/s

2. Restart nis (or a different service as available).
$ sudo /etc/init.d/nis restart
 * Starting NIS service

3. Check what port it got:
$ sudo netstat --inet -nap | grep ypbind
tcp 0 0 0.0.0.0:724 0.0.0.0:* LISTEN 29803/ypbind
udp 0 0 0.0.0.0:723 0.0.0.0:* 29803/ypbind
udp 0 0 0.0.0.0:724 0.0.0.0:* 29803/ypbind

4. Note that it falls in the range 600-999 (there is a small 24 out of 424 chance it will not -- try again).

This also fails with spaces, substituting the perl command in step 1:
$ perl -e 'for ($i=600; $i<1000; $i++) {print "$i # foo\n";}' | sudo dd of=/etc/bindresvport.blacklist

It works with the '#' character directly following the port number:
$ perl -e 'for ($i=600; $i<1000; $i++) {print "$i# foo\n";}' | sudo dd of=/etc/bindresvport.blacklist
or with the port number on the line by itself:
$ perl -e 'for ($i=600; $i<1000; $i++) {print "$i\n";}' | sudo dd of=/etc/bindresvport.blacklist

In the last two cases I get:
tcp 0 0 0.0.0.0:1001 0.0.0.0:* LISTEN 29923/ypbind
udp 0 0 0.0.0.0:1000 0.0.0.0:* 29923/ypbind
udp 0 0 0.0.0.0:1001 0.0.0.0:* 29923/ypbind

The comment #3 also describes the fix.

Changed in glibc (Ubuntu):
status: Incomplete → New
Changed in glibc (Ubuntu):
status: New → Confirmed
Changed in glibc (Debian):
status: Unknown → New
Changed in glibc (Debian):
status: New → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.