Skip to content

Commit

Permalink
OS-5792 lxbrand SIGEV_THREAD_ID timers inappropriately reuse allocation
Browse files Browse the repository at this point in the history
Reviewed by: Ryan Zezeski <rpz@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
  • Loading branch information
pfmooney committed Nov 29, 2016
1 parent 12fff98 commit 6bd01dd
Show file tree
Hide file tree
Showing 10 changed files with 429 additions and 327 deletions.
152 changes: 0 additions & 152 deletions usr/src/lib/brand/lx/lx_brand/common/clock.c
Expand Up @@ -26,12 +26,9 @@
*/

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <sys/lx_misc.h>
#include <sys/lx_syscall.h>
Expand Down Expand Up @@ -84,34 +81,6 @@ static int ltos_clock[] = {

#define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0]))

#define LX_SIGEV_PAD_SIZE ((64 - \
(sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int))

typedef struct {
union sigval lx_sigev_value; /* same layout for both */
int lx_sigev_signo;
int lx_sigev_notify;
union {
int lx_pad[LX_SIGEV_PAD_SIZE];
int lx_tid;
struct {
void (*lx_notify_function)(union sigval);
void *lx_notify_attribute;
} lx_sigev_thread;
} lx_sigev_un;
} lx_sigevent_t;

/* sigevent sigev_notify conversion table */
static int ltos_sigev[] = {
SIGEV_SIGNAL,
SIGEV_NONE,
SIGEV_THREAD,
0, /* Linux skips event 3 */
SIGEV_THREAD /* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
};

#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
#define LX_SIGEV_THREAD_ID 4

long
lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
Expand Down Expand Up @@ -157,127 +126,6 @@ lx_adjtimex(void *tp)
return (-EPERM);
}

/*
* Notification function for use with native SIGEV_THREAD in order to
* emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
* timer mechanism and B_SIGEV_THREAD_ID performs the actual event
* delivery to the appropriate lx tid.
*/
static void
lx_sigev_thread_id(union sigval sival)
{
lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
(void) syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
lev->lx_sigev_signo, lev->lx_sigev_value.sival_ptr);
free(lev);
}


/*
* The illumos timer_create man page says it accepts the following clocks:
* CLOCK_REALTIME (3) wall clock
* CLOCK_VIRTUAL (1) user CPU usage clock - No Backend
* CLOCK_PROF (2) user and system CPU usage clock - No Backend
* CLOCK_HIGHRES (4) non-adjustable, high-resolution clock
* However, in reality the illumos timer_create only accepts CLOCK_REALTIME
* and CLOCK_HIGHRES.
*
* Linux has complicated support for clock IDs. For example, the
* clock_getcpuclockid() function can return a negative clock_id. See the Linux
* source and the comment in include/linux/posix-timers.h (above CLOCKFD) which
* describes clock file descriptors and shows how they map to a virt. or sched.
* clock ID. A process can pass one of these negative IDs to timer_create so we
* need to convert it and we currently only allow CLOCK_PROCESS_CPUTIME_ID
* against the current process as the input.
*/
long
lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid)
{
lx_sigevent_t lev;
struct sigevent sev;

if (clock < 0) {
if (clock != 0xfffffffe)
return (-EINVAL);
clock = CLOCK_RT_SLOT; /* force our use of CLOCK_REALTIME */
}

if (clock >= LX_CLOCK_MAX)
return (-EINVAL);

/* We have to convert the Linux sigevent layout to the illumos layout */
if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0)
return (-EFAULT);

if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX)
return (-EINVAL);

sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify];
sev.sigev_signo = lx_ltos_signo(lev.lx_sigev_signo, 0);
sev.sigev_value = lev.lx_sigev_value;

/*
* The signal number is meaningless in SIGEV_NONE, Linux
* accepts any value. We convert invalid signals to 0 so other
* parts of lx signal handling don't break.
*/
if ((sev.sigev_notify != SIGEV_NONE) && (sev.sigev_signo == 0))
return (-EINVAL);

/*
* Assume all Linux libc implementations map SIGEV_THREAD to
* SIGEV_THREAD_ID and ignore passed-in attributes.
*/
sev.sigev_notify_attributes = NULL;

if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
pid_t caller_pid = getpid();
pid_t target_pid;
lwpid_t ignore;
lx_sigevent_t *lev_copy;

if (lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
&target_pid, &ignore) != 0)
return (-EINVAL);

/*
* The caller of SIGEV_THREAD_ID must be in the same
* process as the target thread.
*/
if (caller_pid != target_pid)
return (-EINVAL);

/*
* Pass the original lx sigevent_t to the native
* notify function so that it may pass it to the lx
* helper thread. It is the responsibility of
* lx_sigev_thread_id() to free lev_copy after the
* information is relayed to lx.
*
* If the calling process is forked without an exec
* after this copy but before the timer fires then
* lev_copy will leak in the child. This is acceptable
* given the rarity of this event, the miniscule
* amount leaked, and the fact that the memory is
* reclaimed when the proc dies. It is firmly in the
* land of "good enough".
*/
lev_copy = malloc(sizeof (lx_sigevent_t));
if (lev_copy == NULL)
return (-ENOMEM);

if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
free(lev_copy);
return (-EFAULT);
}

sev.sigev_notify_function = lx_sigev_thread_id;
sev.sigev_value.sival_ptr = lev_copy;
}

return ((timer_create(ltos_clock[clock], &sev, tid) < 0) ? -errno : 0);
}

long
lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
struct itimerspec *old_val)
Expand Down
4 changes: 2 additions & 2 deletions usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
Expand Up @@ -1233,7 +1233,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 219: restart_syscall */
lx_semtimedop, /* 220: semtimedop */
NULL, /* 221: fadvise64 */
lx_timer_create, /* 222: timer_create */
NULL, /* 222: timer_create */
lx_timer_settime, /* 223: timer_settime */
lx_timer_gettime, /* 224: timer_gettime */
lx_timer_getoverrun, /* 225: timer_getoverrun */
Expand Down Expand Up @@ -1601,7 +1601,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 256: epoll_wait */
NULL, /* 257: remap_file_pages */
NULL, /* 258: set_tid_address */
lx_timer_create, /* 259: timer_create */
NULL, /* 259: timer_create */
lx_timer_settime, /* 260: timer_settime */
lx_timer_gettime, /* 261: timer_gettime */
lx_timer_getoverrun, /* 262: timer_getoverrun */
Expand Down
1 change: 0 additions & 1 deletion usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
Expand Up @@ -71,7 +71,6 @@ extern long lx_capset(uintptr_t, uintptr_t);
extern long lx_clock_nanosleep(int, int flags, struct timespec *,
struct timespec *);
extern long lx_adjtimex(void *);
extern long lx_timer_create(int, struct sigevent *, timer_t *);
extern long lx_timer_settime(timer_t, int, struct itimerspec *,
struct itimerspec *);
extern long lx_timer_gettime(timer_t, struct itimerspec *);
Expand Down
56 changes: 0 additions & 56 deletions usr/src/uts/common/brand/lx/os/lx_brand.c
Expand Up @@ -1468,62 +1468,6 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
return (0);
}

case B_SIGEV_THREAD_ID: {
/*
* Emulate Linux's timer_create(2) SIGEV_THREAD_ID
* notification method. This mechanism is only meant
* for userland threading libraries such as glibc and
* is documented as such. Therefore, assume this is
* only ever invoked for the purpose of alerting a
* Linux threading library. Assume that the tid is a
* member of the caller's process and the signal
* number is valid. See lx_sigev_thread_id() for the
* userland side of this emulation.
*
* The return code from this function is not checked
* by the caller since it executes in an asynchronous
* context and there is nothing much to be done. If
* this function does fail then it will manifest as
* Linux threads waiting for a signal they will never
* receive.
*
* arg1 -- Linux tid
* arg2 -- Linux signal number
* arg3 -- sigval pointer
*/

int native_sig = lx_ltos_signo((int)arg2, 0);
pid_t spid;
int stid;
sigqueue_t *sqp;

if (native_sig == 0)
return (EINVAL);

if (lx_lpid_to_spair((pid_t)arg1, &spid, &stid) != 0) {
return (ESRCH);
}
sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
mutex_enter(&curproc->p_lock);
if ((t = idtot(curproc, stid)) == NULL) {
mutex_exit(&curproc->p_lock);
kmem_free(sqp, sizeof (sigqueue_t));
return (ESRCH);
}

sqp->sq_info.si_signo = native_sig;
sqp->sq_info.si_code = SI_TIMER;
sqp->sq_info.si_pid = curproc->p_pid;
sqp->sq_info.si_zoneid = getzoneid();
sqp->sq_info.si_uid = crgetruid(CRED());
sqp->sq_info.si_value.sival_ptr = (void *)arg3;
sigaddqa(curproc, t, sqp);

mutex_exit(&curproc->p_lock);

return (0);
}

case B_PTRACE_STOP_FOR_OPT:
return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
B_FALSE : B_TRUE, (ulong_t)arg3, arg4));
Expand Down
4 changes: 2 additions & 2 deletions usr/src/uts/common/brand/lx/os/lx_syscall.c
Expand Up @@ -778,7 +778,7 @@ lx_sysent_t lx_sysent32[] = {
{"epoll_wait", lx_epoll_wait, 0, 4}, /* 256 */
{"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 257 */
{"set_tid_address", lx_set_tid_address, 0, 1}, /* 258 */
{"timer_create", NULL, 0, 3}, /* 259 */
{"timer_create", lx_timer_create, 0, 3}, /* 259 */
{"timer_settime", NULL, 0, 4}, /* 260 */
{"timer_gettime", NULL, 0, 2}, /* 261 */
{"timer_getoverrun", NULL, 0, 1}, /* 262 */
Expand Down Expand Up @@ -1112,7 +1112,7 @@ lx_sysent_t lx_sysent64[] = {
{"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */
{"semtimedop", NULL, 0, 4}, /* 220 */
{"fadvise64", lx_fadvise64, 0, 4}, /* 221 */
{"timer_create", NULL, 0, 3}, /* 222 */
{"timer_create", lx_timer_create, 0, 3}, /* 222 */
{"timer_settime", NULL, 0, 4}, /* 223 */
{"timer_gettime", NULL, 0, 2}, /* 224 */
{"timer_getoverrun", NULL, 0, 1}, /* 225 */
Expand Down
2 changes: 1 addition & 1 deletion usr/src/uts/common/brand/lx/sys/lx_brand.h
Expand Up @@ -109,7 +109,7 @@ extern "C" {
#define B_HELPER_SIGQUEUE 145
#define B_HELPER_TGSIGQUEUE 146
#define B_SET_NATIVE_STACK 147
#define B_SIGEV_THREAD_ID 148
/* formerly B_SIGEV_THREAD_ID 148 */
#define B_OVERRIDE_KERN_VER 149
#define B_PTRACE_SIG_RETURN 150
#define B_GET_PERSONALITY 151
Expand Down
1 change: 1 addition & 0 deletions usr/src/uts/common/brand/lx/sys/lx_syscalls.h
Expand Up @@ -237,6 +237,7 @@ extern long lx_syslog();
extern long lx_removexattr();
extern long lx_tgkill();
extern long lx_time();
extern long lx_timer_create();
extern long lx_tkill();
extern long lx_umask();
extern long lx_umount();
Expand Down

0 comments on commit 6bd01dd

Please sign in to comment.