多线程中的uid

在内核级别,用户 ID 和 组 ID 是每个线程的属性,然而,POSIX 标准要求同一个进程里的多个线程共享相同的认证信息(credentials)。
在 NPTL (Native POSIX Threads Library)线程实现中,通过对某些系统调用进行封装,从而支持了这一要求。这里的封装函数包括 setuid(),它通过信号技术确保了当其中某个线程改变认证信息后,其他所有线程也会跟着改变对应的认证信息。

在 glibc 中,setuid() 最终会调用 __nptl_setxid(),其定义在 nptl/allocatestack.c 中:

int
attribute_hidden
__nptl_setxid (struct xid_command *cmdp)                                                                               
{ 
...
      list_for_each (runp, &__stack_user)
    {
      struct pthread *t = list_entry (runp, struct pthread, list);
      if (t == self)
        continue;

      signalled += setxid_signal_thread (cmdp, t);
    }
...
}

上述函数调用了 setxid_signal_thread() 来给其他线程发信号,其定义在 nptl/allocatestack.c 中:

static int
setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
{
  if ((t->cancelhandling & SETXID_BITMASK) == 0)
    return 0;

  int val;
  pid_t pid = __getpid ();
  INTERNAL_SYSCALL_DECL (err);
  val = INTERNAL_SYSCALL_CALL (tgkill, err, pid, t->tid, SIGSETXID);

  /* If this failed, it must have had not started yet or else exited.  */
  if (!INTERNAL_SYSCALL_ERROR_P (val, err))
    {
      atomic_increment (&cmdp->cntr);
      return 1;
    }
  else
    return 0;
}

可以看到上述函数通过 tgkill() 系统调用给当前进程的 t->tid 线程发了一个 SIGSETXID 信号。

SIGSETXID 信号的处理函数是 sighandler_setxid(),其定义在 ./nptl/nptl-init.c 中:

/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
   tell each thread to call the respective setxid syscall on itself.  This is
   the handler.  */
static void
sighandler_setxid (int sig, siginfo_t *si, void *ctx)
{
  int result;
...
  /* Safety check.  It would be possible to call this function for
     other signals and send a signal from another process.  This is not
     correct and might even be a security problem.  Try to catch as
     many incorrect invocations as possible.  */
  if (sig != SIGSETXID
      || si->si_pid != __getpid ()
      || si->si_code != SI_TKILL)
    return;

  INTERNAL_SYSCALL_DECL (err);
  result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0],
                 __xidcmd->id[1], __xidcmd->id[2]);
...
}

上述函数通过 INTERNAL_SYSCALL_NCS() 宏进行了系统调用,其中 __xidcmd->syscall_no 是 setuid() 的系统调用号。
不过,如果在线程中 通过 syscall() 来旁路系统调用后,上述 POSIX 要求就不满足了,例如:

//setuid(1001);
syscall(SYS_setuid, 1001);


 

猜你喜欢

转载自blog.csdn.net/choumin/article/details/113136649