>From 15be8e6bcb6203fdb91d4a01fb65c2d4ef76995e Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Tue, 9 Apr 2019 22:30:16 +0200 Subject: [PATCH] mountlist: make parsing /proc/self/mountinfo more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cater for the following issues with mountinfo parsing (the first one was reported by Zbigniew Jędrzejewski-Szmek in ). 1. The fields source, target, mntroot and fstype may contain characters like '\r'; sscanf(3) fails to read such values with the %s format specifier because it would stop at such characters. Example: "mount -t tmpfs tmpfs /foo^Mbar". The only true separator in that file is the ' ' character. 2. The source field may be an empty string, which happens e.g. with "mount -t tmpfs '' /target". 3. The fstype field may contain mangled characters as well which need unescaping. * lib/mountlist.c (terminate_at_blank): Add utility function. (read_file_system_list): In the block trying to read the mountinfo file, avoid using sscanf(3) with %s format; instead, parse the above fields separated by white spaces one by one. Handle the case when the source field is an empty string. Unescape the fstype field. --- ChangeLog | 22 +++++++++++++ lib/mountlist.c | 83 +++++++++++++++++++++++++++++++------------------ 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 87d95ed99..e049e81a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2019-04-09 Bernhard Voelker + + mountlist: make parsing /proc/self/mountinfo more robust + Cater for the following issues with mountinfo parsing (the first + one was reported by Zbigniew Jędrzejewski-Szmek + in ). + 1. The fields source, target, mntroot and fstype may contain characters + like '\r'; sscanf(3) fails to read such values with the %s format + specifier because it would stop at such characters. + Example: "mount -t tmpfs tmpfs /foo^Mbar". + The only true separator in that file is the ' ' character. + 2. The source field may be an empty string, which happens e.g. with + "mount -t tmpfs '' /target". + 3. The fstype field may contain mangled characters as well which need + unescaping. + * lib/mountlist.c (terminate_at_blank): Add utility function. + (read_file_system_list): In the block trying to read the mountinfo file, + avoid using sscanf(3) with %s format; instead, parse the above fields + separated by white spaces one by one. + Handle the case when the source field is an empty string. + Unescape the fstype field. + 2019-04-09 Bruno Haible openmp: Add workaround for 32-bit programs on AIX. diff --git a/lib/mountlist.c b/lib/mountlist.c index 9b54a2cf7..9e01d13f4 100644 --- a/lib/mountlist.c +++ b/lib/mountlist.c @@ -418,6 +418,18 @@ unescape_tab (char *str) str[j++] = str[i]; } } + +/* Find the next white space in STR, terminate the string there in place, + and return that position. Otherwise return NULL. */ + +static char * +terminate_at_blank (char const *str) +{ + char *s = NULL; + if ((s = strchr (str, ' ')) != NULL) + *s = '\0'; + return s; +} #endif /* Return a list of the currently mounted file systems, or NULL on error. @@ -453,56 +465,65 @@ read_file_system_list (bool need_fs_type) while (getline (&line, &buf_size, fp) != -1) { unsigned int devmaj, devmin; - int target_s, target_e, type_s, type_e; - int source_s, source_e, mntroot_s, mntroot_e; - char test; - char *dash; + int mntroot_s; + char *mntroot, *blank, *target, *dash, *fstype, *source; int rc; rc = sscanf(line, "%*u " /* id - discarded */ "%*u " /* parent - discarded */ "%u:%u " /* dev major:minor */ - "%n%*s%n " /* mountroot */ - "%n%*s%n" /* target, start and end */ - "%c", /* more data... */ + "%n", /* mountroot */ &devmaj, &devmin, - &mntroot_s, &mntroot_e, - &target_s, &target_e, - &test); + &mntroot_s); + + if (rc != 2 && rc != 3) /* 3 if %n included in count. */ + continue; - if (rc != 3 && rc != 7) /* 7 if %n included in count. */ + /* find end of MNTROOT. */ + mntroot = line + mntroot_s; + if (! (blank = terminate_at_blank (mntroot))) + continue; + + /* find end of TARGET. */ + target = blank + 1; + if (! (blank = terminate_at_blank (target))) continue; /* skip optional fields, terminated by " - " */ - dash = strstr (line + target_e, " - "); + dash = strstr (blank + 1, " - "); if (! dash) continue; - rc = sscanf(dash, " - " - "%n%*s%n " /* FS type, start and end */ - "%n%*s%n " /* source, start and end */ - "%c", /* more data... */ - &type_s, &type_e, - &source_s, &source_e, - &test); - if (rc != 1 && rc != 5) /* 5 if %n included in count. */ + /* advance past the " - " separator. */ + fstype = dash + 3; + if (! (blank = terminate_at_blank (fstype))) continue; + source = blank + 1; + if (*source == ' ') + { + /* The source is an empty string, which is e.g. allowed for + tmpfs: "mount -t tmpfs '' /mnt". */ + *source = '\0'; + } + else + { + if (! (blank = terminate_at_blank (source))) + continue; + } + /* manipulate the sub-strings in place. */ - line[mntroot_e] = '\0'; - line[target_e] = '\0'; - dash[type_e] = '\0'; - dash[source_e] = '\0'; - unescape_tab (dash + source_s); - unescape_tab (line + target_s); - unescape_tab (line + mntroot_s); + unescape_tab (source); + unescape_tab (target); + unescape_tab (mntroot); + unescape_tab (fstype); me = xmalloc (sizeof *me); - me->me_devname = xstrdup (dash + source_s); - me->me_mountdir = xstrdup (line + target_s); - me->me_mntroot = xstrdup (line + mntroot_s); - me->me_type = xstrdup (dash + type_s); + me->me_devname = xstrdup (source); + me->me_mountdir = xstrdup (target); + me->me_mntroot = xstrdup (mntroot); + me->me_type = xstrdup (fstype); me->me_type_malloced = 1; me->me_dev = makedev (devmaj, devmin); /* we pass "false" for the "Bind" option as that's only -- 2.21.0