HackRF-Treasure-Chest/Software/Universal Radio Hacker/data/semwraplib.c

280 lines
7.0 KiB
C
Raw Normal View History

2022-09-22 22:46:47 +02:00
// Source: https://github.com/snapcore/snapcraft-preloads/blob/master/semaphores/preload-semaphores.c
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
/*
* $ gcc -Wall -fPIC -shared -o mylib.so ./lib.c -ldl
* $ LD_PRELOAD=./mylib.so ...
*/
#include <dlfcn.h>
#include <sys/types.h>
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <limits.h>
static sem_t *(*original_sem_open) (const char *, int, ...);
static int (*original_sem_unlink) (const char *);
// Format is: 'sem.snap.SNAP_NAME.<something>'. So: 'sem.snap.' + '.' = 10
#define MAX_NAME_SIZE NAME_MAX - 10
#define SHM_DIR "/dev/shm"
void debug(char *s, ...)
{
if (secure_getenv("SEMWRAP_DEBUG")) {
va_list va;
va_start(va, s);
fprintf(stderr, "SEMWRAP: ");
vfprintf(stderr, s, va);
va_end(va);
fprintf(stderr, "\n");
}
}
const char *get_snap_name(void)
{
const char *snapname = getenv("SNAP_INSTANCE_NAME");
if (!snapname) {
snapname = getenv("SNAP_NAME");
}
if (!snapname) {
debug("SNAP_NAME and SNAP_INSTANCE_NAME not set");
}
return snapname;
}
int rewrite(const char *snapname, const char *name, char *rewritten,
size_t rmax)
{
if (strlen(snapname) + strlen(name) > MAX_NAME_SIZE) {
errno = ENAMETOOLONG;
return -1;
}
const char *tmp = name;
if (tmp[0] == '/') {
// If specified with leading '/', just strip it to avoid
// having to mkdir(), etc
tmp = &name[1];
}
int n = snprintf(rewritten, rmax, "snap.%s.%s", snapname, tmp);
if (n < 0 || n >= rmax) {
fprintf(stderr, "snprintf truncated\n");
return -1;
}
rewritten[rmax-1] = '\0';
return 0;
}
sem_t *sem_open(const char *name, int oflag, ...)
{
mode_t mode;
unsigned int value;
debug("sem_open()");
debug("requested name: %s", name);
// lookup the libc's sem_open() if we haven't already
if (!original_sem_open) {
dlerror();
original_sem_open = dlsym(RTLD_NEXT, "sem_open");
if (!original_sem_open) {
debug("could not find sem_open in libc");
return SEM_FAILED;
}
dlerror();
}
// mode and value must be set with O_CREAT
va_list argp;
va_start(argp, oflag);
if (oflag & O_CREAT) {
mode = va_arg(argp, mode_t);
value = va_arg(argp, unsigned int);
if (value > SEM_VALUE_MAX) {
errno = EINVAL;
return SEM_FAILED;
}
}
va_end(argp);
const char *snapname = get_snap_name();
// just call libc's sem_open() if snapname not set
if (!snapname) {
if (oflag & O_CREAT) {
return original_sem_open(name, oflag, mode, value);
}
return original_sem_open(name, oflag);
}
// Format the rewritten name
char rewritten[MAX_NAME_SIZE+1];
if (rewrite(snapname, name, rewritten, MAX_NAME_SIZE + 1) != 0) {
return SEM_FAILED;
}
debug("rewritten name: %s", rewritten);
if (oflag & O_CREAT) {
// glibc's sem_open with O_CREAT will create a file in /dev/shm
// by creating a tempfile, initializing it, hardlinking it and
// unlinking the tempfile. We:
// 1. create a temporary file in /dev/shm with rewritten path
// as the template and the specified mode
// 2. initializing a sem_t with sem_init
// 3. writing the initialized sem_t to the temporary file using
// sem_open()s declared value. We used '1' for pshared since
// that is how glibc sets up a named semaphore
// 4. close the temporary file
// 5. hard link the temporary file to the rewritten path. If
// O_EXCL is not specified, ignore EEXIST and just cleanup
// as per documented behavior in 'man sem_open'. If O_EXCL
// is specified and file exists, exit with error. If link is
// successful, cleanup.
// 6. call glibc's sem_open() without O_CREAT|O_EXCL
//
// See glibc's fbtl/sem_open.c for more details
// First, calculate the requested path
char path[PATH_MAX] = { 0 };
// /sem. + '/0' = 14
int max_path_size = strlen(SHM_DIR) + strlen(rewritten) + 6;
if (max_path_size >= PATH_MAX) {
// Should never happen since PATH_MAX should be much
// larger than NAME_MAX, but be defensive.
errno = ENAMETOOLONG;
return SEM_FAILED;
}
int n = snprintf(path, max_path_size, "%s/sem.%s", SHM_DIR,
rewritten);
if (n < 0 || n >= max_path_size) {
errno = ENAMETOOLONG;
return SEM_FAILED;
}
path[max_path_size-1] = '\0';
// Then calculate the template path
char tmp[PATH_MAX] = { 0 };
n = snprintf(tmp, PATH_MAX, "%s/%s.XXXXXX", SHM_DIR,
rewritten);
if (n < 0 || n >= PATH_MAX) {
errno = ENAMETOOLONG;
return SEM_FAILED;
}
tmp[PATH_MAX-1] = '\0';
// Next, create a temporary file
int fd = mkstemp(tmp);
if (fd < 0) {
return SEM_FAILED;
}
debug("tmp name: %s", tmp);
// Update the temporary file to have the requested mode
if (fchmod(fd, mode) < 0) {
close(fd);
unlink(tmp);
return SEM_FAILED;
}
// Then write out an empty semaphore and set the initial value.
// We use '1' for pshared since that is how glibc sets up the
// semaphore (see glibc's fbtl/sem_open.c)
sem_t initsem;
sem_init(&initsem, 1, value);
if (write(fd, &initsem, sizeof(sem_t)) < 0) {
close(fd);
unlink(tmp);
return SEM_FAILED;
}
close(fd);
// Then link the file into place. If the target exists and
// O_EXCL was not specified, just cleanup and proceed to open
// the existing file as per documented behavior in 'man
// sem_open'.
int existed = 0;
if (link(tmp, path) < 0) {
// Note: snapd initially didn't allow 'l' in its
// policy so we first try with link() since it is
// race-free but fallback to rename() if necessary.
if (errno == EACCES || errno == EPERM) {
fprintf(stderr, "sem_open() wrapper: hard linking tempfile denied. Falling back to rename()\n");
if (rename(tmp, path) < 0) {
unlink(tmp);
return SEM_FAILED;
}
} else if (oflag & O_EXCL || errno != EEXIST) {
unlink(tmp);
return SEM_FAILED;
}
existed = 1;
}
unlink(tmp);
// Then call sem_open() on the created file, stripping out the
// O_CREAT|O_EXCL since we just created it
sem_t *sem = original_sem_open(rewritten,
oflag & ~(O_CREAT | O_EXCL));
if (sem == SEM_FAILED) {
if (!existed) {
unlink(path);
}
return SEM_FAILED;
}
return sem;
} else {
// without O_CREAT, just call sem_open with rewritten
return original_sem_open(rewritten, oflag);
}
return SEM_FAILED;
}
// sem_unlink
int sem_unlink(const char *name)
{
debug("sem_unlink()");
debug("requested name: %s", name);
// lookup the libc's sem_unlink() if we haven't already
if (!original_sem_unlink) {
dlerror();
original_sem_unlink = dlsym(RTLD_NEXT, "sem_unlink");
if (!original_sem_unlink) {
debug("could not find sem_unlink in libc");
return -1;
}
dlerror();
}
const char *snapname = get_snap_name();
// just call libc's sem_unlink() if snapname not set
if (!snapname) {
return original_sem_unlink(name);
}
// Format the rewritten name
char rewritten[MAX_NAME_SIZE+1];
if (rewrite(snapname, name, rewritten, MAX_NAME_SIZE + 1) != 0) {
return -1;
}
debug("rewritten name: %s", rewritten);
return original_sem_unlink(rewritten);
}