Hi, I'm having a problem where calling avc_destroy doesn't seem to close the netlink socket, because a subsequent avc_init is unable to bind to the socket, and gets an error "Address already in use". The attached test program lets me reproduce the problem - the very interesting thing is it seems to only happen about 50% of the time. Is there some race here in the kernel? As far as I can tell the close() is being called so the socket should be shut down.
#include <errno.h> #include <pthread.h> #include <syslog.h> #include <selinux/selinux.h> #include <selinux/avc.h> #include <selinux/av_permissions.h> #include <selinux/flask.h> #include <signal.h> #include <string.h> #include <stdio.h> #include <stdarg.h> #define HAVE_SELINUX #ifdef HAVE_SELINUX /* Store the value telling us if SELinux is enabled in the kernel. */ static int selinux_enabled = 0; /* Store an avc_entry_ref to speed AVC decisions. */ static struct avc_entry_ref aeref; /* Store the SID of the bus itself to use as the default. */ static security_id_t bus_sid = SECSID_WILD; /* Thread to listen for SELinux status changes via netlink. */ static pthread_t avc_notify_thread; /* Prototypes for AVC callback functions. */ static void log_callback (const char *fmt, ...); static void *avc_create_thread (void (*run) (void)); static void avc_stop_thread (void *thread); static void *avc_alloc_lock (void); static void avc_get_lock (void *lock); static void avc_release_lock (void *lock); static void avc_free_lock (void *lock); /* AVC callback structures for use in avc_init. */ static const struct avc_memory_callback mem_cb = { .func_malloc = malloc, .func_free = free }; static const struct avc_log_callback log_cb = { .func_log = log_callback, .func_audit = NULL }; static const struct avc_thread_callback thread_cb = { .func_create_thread = avc_create_thread, .func_stop_thread = avc_stop_thread }; static const struct avc_lock_callback lock_cb = { .func_alloc_lock = avc_alloc_lock, .func_get_lock = avc_get_lock, .func_release_lock = avc_release_lock, .func_free_lock = avc_free_lock }; #endif /* HAVE_SELINUX */ /** * Log callback to log denial messages from the AVC. * This is used in avc_init. Logs to both standard * error and syslogd. * * @param fmt the format string * @param variable argument list */ #ifdef HAVE_SELINUX static void log_callback (const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsyslog (LOG_INFO, fmt, ap); va_end(ap); } /** * On a policy reload we need to reparse the SELinux configuration file, since * this could have changed. Send a SIGHUP to reload all configs. */ static int policy_reload_callback (u_int32_t event, security_id_t ssid, security_id_t tsid, security_class_t tclass, access_vector_t perms, access_vector_t *out_retained) { if (event == AVC_CALLBACK_RESET) return raise (SIGHUP); return 0; } /** * Create thread to notify the AVC of enforcing and policy reload * changes via netlink. * * @param run the thread run function * @return pointer to the thread */ static void * avc_create_thread (void (*run) (void)) { int rc; rc = pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL); if (rc != 0) { fprintf (stderr, "Failed to start AVC thread: %s\n", strerror (rc)); exit (1); } return &avc_notify_thread; } /* Stop AVC netlink thread. */ static void avc_stop_thread (void *thread) { pthread_cancel (*(pthread_t *) thread); } /* Allocate a new AVC lock. */ static void * avc_alloc_lock (void) { pthread_mutex_t *avc_mutex; avc_mutex = malloc (sizeof (pthread_mutex_t)); if (avc_mutex == NULL) { fprintf (stderr, "Could not create mutex: %s\n", strerror (errno)); exit (1); } pthread_mutex_init (avc_mutex, NULL); return avc_mutex; } /* Acquire an AVC lock. */ static void avc_get_lock (void *lock) { pthread_mutex_lock (lock); } /* Release an AVC lock. */ static void avc_release_lock (void *lock) { pthread_mutex_unlock (lock); } /* Free an AVC lock. */ static void avc_free_lock (void *lock) { pthread_mutex_destroy (lock); free (lock); } #endif /* HAVE_SELINUX */ /** * Initialize the user space access vector cache (AVC) for D-BUS and set up * logging callbacks. */ int bus_selinux_init (void) { #ifdef HAVE_SELINUX int r; char *bus_context; /* Determine if we are running an SELinux kernel. */ r = is_selinux_enabled (); if (r < 0) { fprintf (stderr, "Could not tell if SELinux is enabled: %s\n", strerror (errno)); return 0; } selinux_enabled = r != 0; if (!selinux_enabled) { fprintf (stderr, "SELinux not enabled in this kernel.\n"); return 1; } fprintf (stderr, "SELinux is enabled in this kernel.\n"); avc_entry_ref_init (&aeref); if (avc_init ("avc", &mem_cb, &log_cb, &thread_cb, &lock_cb) < 0) { fprintf (stderr, "Failed to start Access Vector Cache (AVC).\n"); return 0; } else { openlog ("dbus", LOG_PERROR, LOG_USER); fprintf (stderr, "Access Vector Cache (AVC) started.\n"); } if (avc_add_callback (policy_reload_callback, AVC_CALLBACK_RESET, NULL, NULL, 0, 0) < 0) { fprintf (stderr, "Failed to add policy reload callback: %s\n", strerror (errno)); avc_destroy (); return 0; } bus_context = NULL; bus_sid = SECSID_WILD; if (getcon (&bus_context) < 0) { fprintf (stderr, "Error getting context of bus: %s\n", strerror (errno)); return 0; } if (avc_context_to_sid (bus_context, &bus_sid) < 0) { fprintf (stderr, "Error getting SID from bus context: %s\n", strerror (errno)); freecon (bus_context); return 0; } freecon (bus_context); return 1; #else return 1; #endif /* HAVE_SELINUX */ } void bus_selinux_shutdown (void) { #ifdef HAVE_SELINUX if (!selinux_enabled) return; sidput (bus_sid); bus_sid = SECSID_WILD; #ifdef DBUS_ENABLE_VERBOSE_MODE bus_avc_print_stats (); #endif /* DBUS_ENABLE_VERBOSE_MODE */ avc_destroy (); #endif /* HAVE_SELINUX */ } int main(int argc, char **argv) { if (!bus_selinux_init ()) { fprintf (stderr, "couldn't init selinux\n"); exit (1); } bus_selinux_shutdown (); if (!bus_selinux_init ()) { fprintf (stderr, "couldn't init selinux\n"); exit (1); } bus_selinux_shutdown (); return 0; }
Attachment:
signature.asc
Description: This is a digitally signed message part