github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/cmd/libsnap-confine-private/locking-test.c (about)

     1  /*
     2   * Copyright (C) 2017 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "locking.h"
    19  #include "locking.c"
    20  
    21  #include "../libsnap-confine-private/cleanup-funcs.h"
    22  #include "../libsnap-confine-private/test-utils.h"
    23  
    24  #include <errno.h>
    25  
    26  #include <glib.h>
    27  #include <glib/gstdio.h>
    28  
    29  // Set alternate locking directory
    30  static void sc_set_lock_dir(const char *dir)
    31  {
    32  	sc_lock_dir = dir;
    33  }
    34  
    35  // A variant of unsetenv that is compatible with GDestroyNotify
    36  static void my_unsetenv(const char *k)
    37  {
    38  	unsetenv(k);
    39  }
    40  
    41  // Use temporary directory for locking.
    42  //
    43  // The directory is automatically reset to the real value at the end of the
    44  // test.
    45  static const char *sc_test_use_fake_lock_dir(void)
    46  {
    47  	char *lock_dir = NULL;
    48  	if (g_test_subprocess()) {
    49  		// Check if the environment variable is set. If so then someone is already
    50  		// managing the temporary directory and we should not create a new one.
    51  		lock_dir = getenv("SNAP_CONFINE_LOCK_DIR");
    52  		g_assert_nonnull(lock_dir);
    53  	} else {
    54  		lock_dir = g_dir_make_tmp(NULL, NULL);
    55  		g_assert_nonnull(lock_dir);
    56  		g_test_queue_free(lock_dir);
    57  		g_assert_cmpint(setenv("SNAP_CONFINE_LOCK_DIR", lock_dir, 0),
    58  				==, 0);
    59  		g_test_queue_destroy((GDestroyNotify) my_unsetenv,
    60  				     "SNAP_CONFINE_LOCK_DIR");
    61  		g_test_queue_destroy((GDestroyNotify) rm_rf_tmp, lock_dir);
    62  	}
    63  	g_test_queue_destroy((GDestroyNotify) sc_set_lock_dir, SC_LOCK_DIR);
    64  	sc_set_lock_dir(lock_dir);
    65  	return lock_dir;
    66  }
    67  
    68  // Check that locking a namespace actually flock's the mutex with LOCK_EX
    69  static void test_sc_lock_unlock(void)
    70  {
    71  	if (geteuid() != 0) {
    72  		g_test_skip("this test only runs as root");
    73  		return;
    74  	}
    75  
    76  	const char *lock_dir = sc_test_use_fake_lock_dir();
    77  	int fd = sc_lock_generic("foo", 123);
    78  	// Construct the name of the lock file
    79  	char *lock_file SC_CLEANUP(sc_cleanup_string) = NULL;
    80  	lock_file = g_strdup_printf("%s/foo.123.lock", lock_dir);
    81  	// Open the lock file again to obtain a separate file descriptor.
    82  	// According to flock(2) locks are associated with an open file table entry
    83  	// so this descriptor will be separate and can compete for the same lock.
    84  	int lock_fd SC_CLEANUP(sc_cleanup_close) = -1;
    85  	lock_fd = open(lock_file, O_RDWR | O_CLOEXEC | O_NOFOLLOW);
    86  	g_assert_cmpint(lock_fd, !=, -1);
    87  	// The non-blocking lock operation should fail with EWOULDBLOCK as the lock
    88  	// file is locked by sc_nlock_ns_mutex() already.
    89  	int err = flock(lock_fd, LOCK_EX | LOCK_NB);
    90  	int saved_errno = errno;
    91  	g_assert_cmpint(err, ==, -1);
    92  	g_assert_cmpint(saved_errno, ==, EWOULDBLOCK);
    93  	// Unlock the lock.
    94  	sc_unlock(fd);
    95  	// Re-attempt the locking operation. This time it should succeed.
    96  	err = flock(lock_fd, LOCK_EX | LOCK_NB);
    97  	g_assert_cmpint(err, ==, 0);
    98  }
    99  
   100  // Check that holding a lock is properly detected.
   101  static void test_sc_verify_snap_lock__locked(void)
   102  {
   103  	if (geteuid() != 0) {
   104  		g_test_skip("this test only runs as root");
   105  		return;
   106  	}
   107  
   108  	(void)sc_test_use_fake_lock_dir();
   109  	int fd = sc_lock_snap("foo");
   110  	sc_verify_snap_lock("foo");
   111  	sc_unlock(fd);
   112  }
   113  
   114  // Check that holding a lock is properly detected.
   115  static void test_sc_verify_snap_lock__unlocked(void)
   116  {
   117  	if (geteuid() != 0) {
   118  		g_test_skip("this test only runs as root");
   119  		return;
   120  	}
   121  
   122  	(void)sc_test_use_fake_lock_dir();
   123  	if (g_test_subprocess()) {
   124  		sc_verify_snap_lock("foo");
   125  		return;
   126  	}
   127  	g_test_trap_subprocess(NULL, 0, 0);
   128  	g_test_trap_assert_failed();
   129  	g_test_trap_assert_stderr
   130  	    ("unexpectedly managed to acquire exclusive lock over snap foo\n");
   131  }
   132  
   133  static void test_sc_enable_sanity_timeout(void)
   134  {
   135  	if (geteuid() != 0) {
   136  		g_test_skip("this test only runs as root");
   137  		return;
   138  	}
   139  
   140  	if (g_test_subprocess()) {
   141  		sc_enable_sanity_timeout();
   142  		debug("waiting...");
   143  		usleep(7 * G_USEC_PER_SEC);
   144  		debug("woke up");
   145  		sc_disable_sanity_timeout();
   146  		return;
   147  	}
   148  	g_test_trap_subprocess(NULL, 1 * G_USEC_PER_SEC,
   149  			       G_TEST_SUBPROCESS_INHERIT_STDERR);
   150  	g_test_trap_assert_failed();
   151  	g_test_trap_assert_stderr
   152  	    ("sanity timeout expired: Interrupted system call\n");
   153  }
   154  
   155  static void __attribute__((constructor)) init(void)
   156  {
   157  	g_test_add_func("/locking/sc_lock_unlock", test_sc_lock_unlock);
   158  	g_test_add_func("/locking/sc_enable_sanity_timeout",
   159  			test_sc_enable_sanity_timeout);
   160  	g_test_add_func("/locking/sc_verify_snap_lock__locked",
   161  			test_sc_verify_snap_lock__locked);
   162  	g_test_add_func("/locking/sc_verify_snap_lock__unlocked",
   163  			test_sc_verify_snap_lock__unlocked);
   164  }