diff options
author | Simon McVittie <smcv@collabora.com> | 2021-02-16 18:51:23 +0000 |
---|---|---|
committer | Alexander Larsson <alexander.larsson@gmail.com> | 2021-04-16 08:57:20 +0200 |
commit | cb47d83b7221de7f08a487dd47e69eec57f458e5 (patch) | |
tree | 6e9eb082a683eb052edd2d1997d01f99a7ed23a1 /tests/test-instance.c | |
parent | 70a3e44fb59acd9067ca8148931eb3e23acb15b1 (diff) | |
download | flatpak-cb47d83b7221de7f08a487dd47e69eec57f458e5.tar.gz |
run: Add option to share /dev/shm between instances of an app-ID
Similar to /tmp, applications might well use /dev/shm as an IPC
rendezvous between instances, which wouldn't have worked without
--device=shm until now.
Because /dev/shm has specific characteristics (in particular it's
meant to always be a tmpfs), we offload the actual storage into a
subdirectory of the real /dev/shm. Because /dev/shm is a shared
directory between all uids, we have to be extra-careful how we
do this, which is why the test coverage here is important.
This is done on an opt-in basis because of its extra complexity.
Signed-off-by: Simon McVittie <smcv@collabora.com>
Diffstat (limited to 'tests/test-instance.c')
-rw-r--r-- | tests/test-instance.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/tests/test-instance.c b/tests/test-instance.c index 2e6e8a37..411602e9 100644 --- a/tests/test-instance.c +++ b/tests/test-instance.c @@ -33,6 +33,7 @@ #include "flatpak-run-private.h" #include "libglnx/libglnx.h" +#include "libglnx/tests/libglnx-testlib.h" #include "testlib.h" @@ -215,6 +216,274 @@ test_gc (void) g_spawn_close_pid (pid); } +static void +test_claim_per_app_temp_directory (void) +{ + /* Run in a temporary directory so we can create a bunch of symlinks */ + _GLNX_TEST_SCOPED_TEMP_DIR; + + gboolean ok; + glnx_autofd int lock_fd = -1; + glnx_autofd int fd = -1; + g_autofree char *result = NULL; + g_autofree char *flag_path = NULL; + g_autofree char *symlink_path = NULL; + g_autofree char *non_directory_path = NULL; + g_autofree char *dir_in_tmp = NULL; + g_autoptr(GError) error = NULL; + struct stat stat_buf; + + /* In real life this would be the per-app-ID lock, but in fact + * we just need some sort of file descriptor - as currently + * implemented, we don't even need to lock it. */ + lock_fd = open ("mock-per-app-id-lock", + O_CLOEXEC | O_CREAT | O_NOCTTY | O_NOFOLLOW, + 0600); + g_assert_no_errno (lock_fd >= 0 ? 0 : -1); + + /* This emulates the sort of directory that we want to reuse. */ + dir_in_tmp = g_strdup ("/tmp/flatpak-com.example.App-XXXXXX"); + g_assert_nonnull (g_mkdtemp (dir_in_tmp)); + + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "doesnt-exist", + "/tmp", + &result, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* If link_path is a symlink to a directory not in /tmp, we refuse + * to reuse it */ + g_assert_no_errno (symlink ("/nope", "bad-prefix")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "bad-prefix", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, "/nope does not start with /tmp"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* Similar */ + g_assert_no_errno (symlink ("/tmptation", "bad-prefix2")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "bad-prefix2", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, "/tmptation does not start with /tmp/"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* If link_path points to a subdirectory of /tmp that doesn't match the + * expected pattern, we refuse to reuse it */ + g_assert_no_errno (symlink ("/tmp/nope", "bad-prefix3")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "bad-prefix3", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, "/tmp/nope does not start with /tmp/flatpak-"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* Similar */ + g_assert_no_errno (symlink ("/tmp/flatpak-/nope", "too-many-levels")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "too-many-levels", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, + "/tmp/flatpak-/nope has too many directory separators"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* Similar */ + g_assert_no_errno (symlink ("/tmp/flatpak-abc/", "too-many-levels2")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "too-many-levels2", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, + "/tmp/flatpak-abc/ has too many directory separators"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + g_assert_no_errno (symlink ("/tmp/flatpak-org.example.Other-XXXXXX", "wrong-app")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "wrong-app", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, + "/tmp/flatpak-org.example.Other-XXXXXX does not " + "start with /tmp/flatpak-com.example.App"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + g_assert_no_errno (symlink ("/tmp/flatpak-com.example.ApparentlyNot", "wrong-app2")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "wrong-app2", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, + "/tmp/flatpak-com.example.ApparentlyNot does not " + "start with /tmp/flatpak-com.example.App-"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* If it points to a filesystem object matching the right pattern, but + * that is not a directory, we refuse to reuse it */ + non_directory_path = g_strdup ("/tmp/flatpak-com.example.App-XXXXXX"); + g_assert_no_errno ((fd = g_mkstemp (non_directory_path))); + g_assert_no_errno (symlink (non_directory_path, "not-a-directory")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "not-a-directory", + "/tmp", + &result, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* Reuse @non_directory_path as the name of a symlink to a directory: + * we consider that to be equally invalid. Create it inside our + * directory in /tmp so that we can rename() it into place, + * because symlink() does not overwrite, but rename() does. */ + symlink_path = g_build_filename (dir_in_tmp, "symlink", NULL); + g_assert_no_errno (symlink (dir_in_tmp, symlink_path)); + /* Overwrite the file with the symlink */ + g_assert_no_errno (rename (symlink_path, non_directory_path)); + + /* We'll refuse to follow the symlink: for all we know it could be + * attacker-controlled. */ + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "not-a-directory", + "/tmp", + &result, + &error); + + /* Either of these would be reasonable */ + if (error->code == G_IO_ERROR_TOO_MANY_LINKS) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS); + else + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY); + + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* If link_path points to a directory owned by someone else, we refuse + * to use it. This part of the test will be skipped unless you pre-create + * this directory as root. */ + if (stat ("/tmp/flatpak-com.example.App-OwnedByRoot", &stat_buf) == 0 + && stat_buf.st_uid == 0 + && geteuid () != 0) + { + g_assert_no_errno (symlink ("/tmp/flatpak-com.example.App-OwnedByRoot", + "not-our-directory")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "not-our-directory", + "/tmp", + &result, + &error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, + "/tmp/flatpak-com.example.App-OwnedByRoot does not " + "belong to this user"); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + } + + glnx_close_fd (&fd); + g_assert_no_errno (unlink (non_directory_path)); + g_clear_pointer (&non_directory_path, g_free); + + /* Even when we have a symlink to a directory matching the right pattern + * that we own, if it doesn't contain the flag file that indicates that + * it's one of our temp directories, we'll still refuse to use it. */ + g_assert_no_errno (symlink (dir_in_tmp, "good-symlink")); + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "good-symlink", + "/tmp", + &result, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_true (g_str_has_prefix (error->message, + "opening flag file /tmp/flatpak-com.example.App-")); + g_assert_nonnull (strstr (error->message, "/.flatpak-tmpdir:")); + g_assert_null (result); + g_assert_false (ok); + g_clear_error (&error); + + /* Create the flag file (of course in real life this would have happened + * much sooner) */ + flag_path = g_build_filename (dir_in_tmp, ".flatpak-tmpdir", NULL); + g_file_set_contents (flag_path, "", 0, &error); + g_assert_no_error (error); + + /* Now we are finally willing to reuse the directory! A happy ending + * at last. */ + ok = flatpak_instance_claim_per_app_temp_directory ("com.example.App", + lock_fd, + AT_FDCWD, + "good-symlink", + "/tmp", + &result, + &error); + g_assert_no_error (error); + g_assert_cmpstr (result, ==, dir_in_tmp); + g_assert_true (ok); + + g_assert_no_errno (unlink (flag_path)); +} + int main (int argc, char *argv[]) { @@ -225,6 +494,8 @@ main (int argc, char *argv[]) g_test_init (&argc, &argv, NULL); g_test_add_func ("/instance/gc", test_gc); + g_test_add_func ("/instance/claim-per-app-temp-directory", + test_claim_per_app_temp_directory); res = g_test_run (); |