cmd: add mount / unmount helpers #2658

Closed
wants to merge 18 commits into
from

Conversation

Projects
None yet
4 participants
Contributor

zyga commented Jan 18, 2017

This branch adds sc_do_mount and sc_do_umount that mirror mount/umount library functions with
additional logging and switches the tree over to them. This cuts significant and error prone boilerplate
from the code.

zyga added some commits Jan 2, 2017

cmd/snap-confine: add sc_mount and sc_mount_cmd
This patch adds two functions that aid in commonly performed mount
operations by abbreviating common messaging and error handling.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/snap-confine: use sc_do_mount everywhere
This patch removes some boilerplate code around all the mount calls by
replacing them with a call to sc_do_mount() that does automatic logging
and error checks.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/snap-confine: add sc_umount_cmd
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/snap-confine: add sc_do_umount
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/snap-confine: use sc_do_umount everywhere
This patch removes some boilerplate code around all the umount calls by
replacing them with a call to sc_do_umount() that does automatic logging
and error checks.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/snap-confine/mount-opt.c
+ special = " --bind";
+ }
+ used_special_flags |= MS_BIND | MS_REC;
+ } else if (mountflags & MS_MOVE) {
@mvo5

mvo5 Jan 19, 2017

Collaborator

Do we need the else if here? If the user gives a combination that is not part of this if/else if/... tree (maybe by accident) we won't get accurate logging? Or is this no concern for some reason?

@zyga

zyga Jan 19, 2017

Contributor

Interesting combination. I was thinking that perhaps apart from logging all the options as the appear (even if they make no sense) we could check if the arguments make sense and die() if they dont.

Looks good, just one question about the logging

zyga added some commits Jan 20, 2017

cmd: add sc_grow_string
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: don't use strncat (evil)
So strncat is evil, I incorrectly assumed that n refers to the size
of the destination buffer, not the size of the source buffer.
I've switched the code to use the home-grown sc_grow_string() which
doesn't have this problem.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: add paranoia test for sc_mount_cmd()
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd/libsnap-confine-private/mount-opt.c
@@ -115,3 +117,122 @@ const char *sc_mount_opt2str(unsigned long flags)
}
return buf;
}
+
+static void sc_grow_string(char **s, const char *extra)
@mvo5

mvo5 Jan 20, 2017

Collaborator

I'm slightly concerned about the direction we are talking with snap-confine. It is a suid root binary and we want it to be small and easy. Functions like this make me uneasy. It feels like we re-inventing low-level library. Sorry for being so negative but I think we should think about alternatives, maybe just using simpler logging?

@zyga

zyga Jan 20, 2017

Contributor

When we're processing mount profiles we can see almost arbitrary mounts. I agree this is extra complexity but I think it is at some level unavoidable (and better than a static buffer one can overflow). One alternative would be to remove all logging entirely but I think this hurts debuggability -- often that list of mount operations is very useful when exploring unexpected behavior.

@zyga

zyga Jan 20, 2017

Contributor

After discussing this on telegram we agreed to try a simpler constant size-buffer.

zyga added some commits Jan 20, 2017

cmd: correct logged error message
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: switch to stpcat
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: switch more code to stpcpy
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: don't free results from sc_{u,}mount_cmd
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
cmd: move sc_do_{u,}mount to common library
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

In general, I like the cleanup of sc_do_umount() and sc_do_mount() since it makes the code clearer and cleaner, but I'd like to see the string handling cleaned up, the debug output conditional and the removal of the static variable.

+{
+ // NOTE: this uses static buffer because it has lower complexity than a
+ // dynamically allocated buffer. We've decided as a team to prefer this
+ // approach.
@jdstrand

jdstrand Jan 26, 2017

Contributor

We have? Using static here means that if sc_mount_cmd() is called a second time, buf will have whatever contents are already present. Because of how you initialize 'to' to 'buf', this works out but there is no reason I can think of where sc_mount_cmd() would benefit from this behavior and I'd much prefer explicit initialization here. If you follow my debug_mount_command() and debug_umount_command() advise below, you can have these functions initialize the buf to pass into sc_mount_cmd() and sc_umount_cmd() instead.

@zyga

zyga Jan 27, 2017

Contributor

We == several team members. I implemented this to dynamically allocate memory earlier and that version was rejected.

@jdstrand

jdstrand Jan 27, 2017

Contributor

As discussed on IRC, there was a misunderstanding. The team was not advocating a static local variable, they were advocating a statically sized array (ie, a stack variable). While I don't have any issues with properly coded dynamic allocations, since this is cleared up please remove 'static char *buf[BUF_SIZE]' and simply use 'char buf[BUF_SIZE]' as described.

+ // NOTE: this uses static buffer because it has lower complexity than a
+ // dynamically allocated buffer. We've decided as a team to prefer this
+ // approach.
+ static char buf[PATH_MAX * 2 + 1000];
@jdstrand

jdstrand Jan 26, 2017

Contributor

Where did you come up with this calculation?

@zyga

zyga Jan 27, 2017

Contributor

"two paths + some extra space". Believe me I much preferred the version that had a dynamically allocated buffer.

+ if (target != NULL && strcmp(target, "none") != 0) {
+ to = stpcpy(to, " ");
+ to = stpcpy(to, target);
+ }
@jdstrand

jdstrand Jan 26, 2017

Contributor

You have to be careful with stpcpy() in that dst must be large enough to add src + NULL. As a library function, I think we need to be checking to make 100% sure that 'to' is always large enough to add 'src'. Perhaps you want to write a helper function similar to must_snprintf that does this for you.

This might provide inspiration for you:

#define BUF_SIZE PATH_MAX*2+1000
...
char* sc_append_string(char *dst, const char *src, size_t size)
{
        int n;

        char *tmp = strdup(dst);
        if (tmp == NULL)
                die("could not strdup");

        n = snprintf(dst, size, "%s%s", tmp, src);
        free(tmp);
        if (n < 0 || n >= size)
                die("failed to snprintf %s", src);

        return dst;
}
...
    char buf[BUF_SIZE] = {0};
    char *to = buf;
    to = sc_append_string(to, ..., BUF_SIZE);

Edit: in the original comment, the above function was (at the last minute) mistakenly named to must_strncpy(). I've edited it to reflect the intended feedback.

+ // NOTE: this uses static buffer because it has lower complexity than a
+ // dynamically allocated buffer. We've decided as a team to prefer this
+ // approach.
+ static char buf[PATH_MAX + 1000];
@jdstrand

jdstrand Jan 26, 2017

Contributor

Same here.

+{
+ char *mount_cmd =
+ sc_mount_cmd(source, target, filesystemtype, mountflags, data);
+ debug("performing operation: %s", mount_cmd);
@jdstrand

jdstrand Jan 26, 2017

Contributor

I don't care for how we have sc_mount_cmd() and sc_umount_cmd() unconditionally doing all kinds of string operations only for debug output that may not be displayed. IMO, it would be better to create a separate debug_mount() and debug_umount() and have them exit if SNAP_CONFINE_DEBUG is not set and only conditionally compiled with build flags (same for debug() for that matter).

@zyga

zyga Jan 27, 2017

Contributor

Just to get this clear, would you like to have a special debug build of snap-confine and have debug() do nothing unless in that special build?

@jdstrand

jdstrand Jan 27, 2017

Contributor

That is my preference. While putting it behind an env is convenient, an attacker attacking snap-confine and trying to escalate privileges will simply set that variable to get at this part of the codebase.

+ char *mount_cmd =
+ sc_mount_cmd(source, target, filesystemtype, mountflags, data);
+ debug("performing operation: %s", mount_cmd);
+ if (mount(source, target, filesystemtype, mountflags, data) < 0) {
@jdstrand

jdstrand Jan 26, 2017

Contributor

Note, before you did '!= 0' where here you are doing '< 0'. Per man mount, this change is fine as mount will only return -1 or 0.

@zyga

zyga Jan 27, 2017

Contributor

I think this is in line with our earlier plan to try to use < 0 everywhere.

@jdstrand

jdstrand Jan 27, 2017

Contributor

That's fine, just always check the man pages! ;)

@mvo5 mvo5 dismissed their stale review Jan 27, 2017

Reverting review, it was for a earlier revision of the code that looked different

Contributor

zyga commented Jan 30, 2017

I'm closing this for the rework that @jdstrand requested. I'll reopen it shortly.

@zyga zyga closed this Jan 30, 2017

@zyga zyga deleted the zyga:sc-mount-helper branch Aug 22, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment