cmd/libsnap: fix parsing of empty mountinfo fields #4109

Merged
merged 3 commits into from Nov 22, 2017

Conversation

Projects
None yet
5 participants
Contributor

zyga commented Oct 31, 2017

This patch fixes an apparently unexpected kernel behavior whereby
a tmpfs with empty "mount source" (which is customarily set to "none"
by various tools) results in a mountinfo field with two consecutive
spaces. That is, empty fields are just printed as-is.

The parser is adjusted and a new unit test is added to cover the case
where this was discovered.

Signed-off-by: Zygmunt Krynicki zygmunt.krynicki@canonical.com

cmd/libsnap-confine-private/mountinfo.c
+ if (nscanned != 1)
+ return NULL;
+ offset += offset_delta;
+ if (line[offset] == ' ') {
@stolowski

stolowski Oct 31, 2017

Contributor

I'm unclear why is this needed again here? Can you explain?

@zyga

zyga Oct 31, 2017

Contributor

The test checks if the first character of the field is a space. This is only true if the field is empty and so this gets special-cased as scanf style functions cannot parse empty strings with %s format character.

@stolowski

stolowski Oct 31, 2017

Contributor

Ok, sorry, I should be more clear: my question is whether this is needed here if same test is done on line 190?

@zyga

zyga Oct 31, 2017

Contributor

Ah, sorry. I looked at the wrong part of the diff.

This test is to compensate the change that was done to sscanf call. Earlier we parsed %s %n and now we parse %s%n. This makes us scan each field correctly. For a visual look at how this behaves:

The # characters are poison bytes that were not written to by the parse routine.
The @ characters are NUL ('\0') bytes.

zyga@fyke:~/snapd/cmd$ make -j libsnap-confine-private/unit-tests && ./libsnap-confine-private/unit-tests -p /mountinfo/parse_mountinfo_entry/empty_source
gcc -DHAVE_CONFIG_H -I.    -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -D_ENABLE_FAULT_INJECTION -g -O2 -MT libsnap-confine-private/libsnap_confine_private_unit_tests-mountinfo-test.o -MD -MP -MF libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-mountinfo-test.Tpo -c -o libsnap-confine-private/libsnap_confine_private_unit_tests-mountinfo-test.o `test -f 'libsnap-confine-private/mountinfo-test.c' || echo './'`libsnap-confine-private/mountinfo-test.c
gcc -DHAVE_CONFIG_H -I.    -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -D_ENABLE_FAULT_INJECTION -g -O2 -MT libsnap-confine-private/libsnap_confine_private_unit_tests-utils-test.o -MD -MP -MF libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-utils-test.Tpo -c -o libsnap-confine-private/libsnap_confine_private_unit_tests-utils-test.o `test -f 'libsnap-confine-private/utils-test.c' || echo './'`libsnap-confine-private/utils-test.c
mv -f libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-utils-test.Tpo libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-utils-test.Po
mv -f libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-mountinfo-test.Tpo libsnap-confine-private/.deps/libsnap_confine_private_unit_tests-mountinfo-test.Po
gcc -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -D_ENABLE_FAULT_INJECTION -g -O2   -o libsnap-confine-private/unit-tests libsnap-confine-private/libsnap_confine_private_unit_tests-classic-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-cleanup-funcs-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-error-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-fault-injection-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-locking-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-mount-opt-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-mountinfo-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-privs-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-secure-getenv-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-snap-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-string-utils-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-test-utils.o libsnap-confine-private/libsnap_confine_private_unit_tests-test-utils-test.o libsnap-confine-private/libsnap_confine_private_unit_tests-unit-tests-main.o libsnap-confine-private/libsnap_confine_private_unit_tests-unit-tests.o libsnap-confine-private/libsnap_confine_private_unit_tests-utils-test.o -lglib-2.0  -Wl,-Bstatic -lcap -Wl,-Bdynamic
/mountinfo/parse_mountinfo_entry/empty_source: Input buffer (first), with offset arrow
Output buffer (second)
 ------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>################################################################################<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 --------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@#################################################################<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 --------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@#######################<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 --------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@###########<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 ----------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@-@#########<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 ----------------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@@@tmpfs@###<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 -----------------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@@@tmpfs@@##<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 -------------------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@@@tmpfs@@rw<
>================================================================================<
Input buffer (first), with offset arrow
Output buffer (second)
 -------------------------------------------------------------------------------v
>304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs  rw<
>#############/@/snap/test-snapd-content-advanced-plug/x1@rw,relatime@@@tmpfs@@rw<
>================================================================================<

codecov-io commented Oct 31, 2017

Codecov Report

Merging #4109 into master will decrease coverage by <.01%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4109      +/-   ##
==========================================
- Coverage   75.95%   75.95%   -0.01%     
==========================================
  Files         440      440              
  Lines       38427    38427              
==========================================
- Hits        29189    29188       -1     
- Misses       7224     7225       +1     
  Partials     2014     2014
Impacted Files Coverage Δ
interfaces/sorting.go 92.3% <0%> (-1.1%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update db1fc27...98f5748. Read the comment docs.

+static void test_parse_mountinfo_entry__empty_source()
+{
+ const char *line =
+ "304 301 0:45 / /snap/test-snapd-content-advanced-plug/x1 rw,relatime - tmpfs rw";
@jdstrand

jdstrand Oct 31, 2017

Contributor

The two spaces between 'tmpfs' and 'rw': is this guaranteed to denote an empty source? Ie, is it an implementation artifact or is it part of the specification for mountinfo? This seems scarily brittle at first glance.

@zyga

zyga Nov 1, 2017

Contributor

It is just how the kernel is implemented. Reading the specification it is neither violating it nor explicitly explained that it behaves this way. IMO this is a kernel bug but the way countess kernel interfaces are defined it doesn’t stand out. I for one would love to see standardized serializer but this is unlikely to happen anytime soon.

I looked at how this is implemented and it is a %s with some escape logic for spaces and other characters.

zyga added some commits Nov 8, 2017

cmd/libsnap: fix parsing of empty mountinfo fields
This patch fixes an apparently unexpected kernel behavior whereby
a tmpfs with empty "mount source" (which is customarily set to "none"
by various tools) results in a mountinfo field with two consecutive
spaces. That is, empty fields are just printed as-is.

The parser is adjusted and a new unit test is added to cover the case
where this was discovered.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/libsnap: simplify parse_next_string_field
The offset_delta variable doesn't have to leave the function so we can
just use a local for that.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

@zyga zyga closed this Nov 15, 2017

@zyga zyga reopened this Nov 15, 2017

Slightly uneasy as we're not passing the size of line buffer as input to parse_next_string_field or at least verifying that there's still input to consume. In theory this should work, the kernel probably won't pass us some random garbage. Other than that, I didn't spot anything obviously wrong with the code.

@zyga zyga merged commit 633daa7 into snapcore:master Nov 22, 2017

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment