ckernelopenbsd

What is the purpose of uint64_t ps_pledge variable in OpenBSD kernel code?


I am playing with OpenBSD kernel code, especially this file sys/kern/sched_bsd.c.

  void
    schedcpu(void *arg)
    {
......

    LIST_FOREACH(p, &allproc, p_list) {
        /*
         * Increment sleep time (if sleeping). We ignore overflow.
         */
        if (p->p_stat == SSLEEP || p->p_stat == SSTOP)
            p->p_slptime++;
        p->p_pctcpu = (p->p_pctcpu * ccpu) >> FSHIFT;
        /*
         * If the process has slept the entire second,
         * stop recalculating its priority until it wakes up.
         */
        if (p->p_slptime > 1)
            continue;
        SCHED_LOCK(s);
        /*
         * p_pctcpu is only for diagnostic tools such as ps.
         */
....
LIST_FOREACH(TYPE *var, LIST_HEAD *head, LIST_ENTRY NAME);
The macro LIST_FOREACH traverses the list referenced by head in the forward direction, assigning each element in turn to var.

Now, here, p will contain the address of struct proc structure of every process which is in the file sys/sys/proc.h.

Now, again, this structure contains another struct process *p_p structure, which denotes the properties of every process like its pid, flags, threads etc.

    struct proc {
        TAILQ_ENTRY(proc) p_runq;
        LIST_ENTRY(proc) p_list;    /* List of all threads. */
    
        struct  process *p_p;       /* The process of this thread. */
        TAILQ_ENTRY(proc) p_thr_link;   /* Threads in a process linkage. */
    
        TAILQ_ENTRY(proc) p_fut_link;   /* Threads in a futex linkage. */
        struct  futex   *p_futex;   /* Current sleeping futex. */
    
        /* substructures: */
        struct  filedesc *p_fd;     /* copy of p_p->ps_fd */
        struct  vmspace *p_vmspace; /* copy of p_p->ps_vmspace */
#define p_rlimit    p_p->ps_limit->pl_rlimit
    
    ....
    
    ....

Now, structure struct process contains uint64_t ps_plegde.

struct process {
    /*
     * ps_mainproc is the original thread in the process.
     * It's only still special for the handling of p_xstat and
     * some signal and ptrace behaviors that need to be fixed.
     */
    struct  proc *ps_mainproc;
    struct  ucred *ps_ucred;    /* Process owner's identity. */

....

....

    u_short ps_acflag;      /* Accounting flags. */

    uint64_t ps_pledge;
    uint64_t ps_execpledge; 

....

Now, I wrote some modification in void schedcpu() function code.

void
schedcpu(void *arg)
{
    pid_t pid;
    uint64_t pledge_bit;
....

....


    LIST_FOREACH(p, &allproc, p_list) {

    pid=p->p_p->pid;
    pledge_bit=p->p_p->ps_pledge;

    if (pledge_bit) {
            printf("pid: %10d pledge_bit: %10llu pledge_xbit:%10llx\n",pid,pledge_bit,pledge_bit);
}

        /*
         * Increment sleep time (if sleeping). We ignore overflow.
         */
        if (p->p_stat == SSLEEP || p->p_stat == SSTOP)
            p->p_slptime++;
        p->p_pctcpu = (p->p_pctcpu * ccpu) >> FS 
....

Here, Kernel log

pid:      37846 pledge_bit:     393359 pledge_xbit:      6008f
pid:      96037 pledge_bit:     393544 pledge_xbit:      60148
pid:      86032 pledge_bit:     264297 pledge_xbit:      40869
pid:      72264 pledge_bit:     393480 pledge_xbit:      60108
pid:      40102 pledge_bit:          8 pledge_xbit:          8
pid:        841 pledge_bit: 2148162527 pledge_xbit:   800a5bdf
pid:      49970 pledge_bit: 2148096143 pledge_xbit:   8009588f
pid:      68505 pledge_bit:         40 pledge_xbit:         28
pid:      46106 pledge_bit:         72 pledge_xbit:         48
pid:      77690 pledge_bit:     537161 pledge_xbit:      83249
pid:      44005 pledge_bit:     262152 pledge_xbit:      40008
pid:      82731 pledge_bit: 2148096143 pledge_xbit:   8009588f
pid:      71609 pledge_bit:     262472 pledge_xbit:      40148
pid:      54330 pledge_bit:     662063 pledge_xbit:      a1a2f
pid:      77764 pledge_bit:    1052776 pledge_xbit:     101068
pid:        699 pledge_bit: 2148096143 pledge_xbit:   8009588f
pid:      84265 pledge_bit:    1052776 pledge_xbit:     101068

....

....

Now, Is that possible to know which process pledge what permissions, from looking at pledge_bit (decimal or hex values) that I got from above output?

I took pledge hex value of dhclient process i.e 0x8009588f, then, I wrote a sample hello world program with pledge("STDIO",NULL); and again I looked at dmesg and got same pledge_bit for hello world i.e 0x8009588f.

Then, this time I looked at dhclient source code and found out that, dhclient code pledged pledge("stdio inet dns route proc", NULL).

But, then, how is it possible to get same pledge hex bit for different pledge parameters?


Solution

  • I have got my answer after reading source code again and again.

    So, I just want to contribute my learning so that future developers won't face any problem regarding this issue or confusion.

    First understanding:

    I wrote hello world like this,

    void
    main() {
    pledge("STDIO", NULL);   /* wrong use of pledge call */
    printf("Hello world\n");
    }
    

    Correction of above code:

    void
    main() {
    
    if (pledge("stdio", NULL) == -1) {
            printf("Error\n");
    }
    printf("Hello world\n");
    }
    

    I forgot to check the return value from pledge().

    Second understanding:

    dhclient.c code contains pledge() call as:

    int
    main(int argc, char *argv[])
    {
        struct ieee80211_nwid    nwid;
        struct ifreq         ifr;
        struct stat      sb;
        const char      *tail_path = "/etc/resolv.conf.tail";
    ....
    
    ....
    
    
    fork_privchld(ifi, socket_fd[0], socket_fd[1]);
    
    ....
    
    ....
    
    
        if ((cmd_opts & OPT_FOREGROUND) == 0) {
            if (pledge("stdio inet dns route proc", NULL) == -1)
                fatal("pledge");
        } else {
            if (pledge("stdio inet dns route", NULL) == -1)
                fatal("pledge");
        }
    
    ....
    
    ....
    
    void
    fork_privchld(struct interface_info *ifi, int fd, int fd2)
    {
        struct pollfd    pfd[1];
        struct imsgbuf  *priv_ibuf;
        ssize_t      n;
        int      ioctlfd, routefd, nfds, rslt;
    
        switch (fork()) {
        case -1:
            fatal("fork");
            break;
        case 0:
            break;
        default:
            return;
        }
    ....
    
    ....
    
    }
    

    Now, I wrote sample hello world code which contains the same parameters as dhclient:

    void
    main() {
    
    if(pledge("stdio inet proc route dns", NULL) == -1) {
            printf("Error\n");
    }
    
    while(1) {}
    }
    

    Now, I tested this above sample hello world code and got pledge_bit as 0x101068 in hex, which is correct.

    But, as I told you earlier in my question, I became confused, when I saw different pledge_bit for dhclient. Because, as you guys know that both have same parameters in their pledge().

    Then, how is it possible?

    Now, here is the catch,

    After continuously looking at dhclient source code, I found one function named as fork_privchld().

    This function called before pledging in dhclient, so, it's like there is no pledge for this function as it is called before pledging.

    So, just for verification again I wrote sample hello world code, but this time without any pledge() syscall.

    void
    main() {
    printf("hello\n");
    }
    

    And, guess what, I got the same pledge_bit as 0x8009588f.

    So, after this verification, it is verified that fork_privchld() function doesn't have any pledge bit set because as it is called before pledging in dhclient.

    This function creates a [priv] child process for dhclient.

    # ps aux|grep dhclient
    root     26416  0.0  0.1   608   544 ??  Is     8:36AM    0:00.00 dhclient: em0 [priv] (dhclient)
    _dhcp    33480  0.0  0.1   744   716 ??  Isp    8:36AM    0:00.00 dhclient: em0 (dhclient)
    

    And, I don't know why I was only looking at the first process i.e [priv] (dhclient). This process is the one which is created by fork_privchld() function.

    That's why this process has 0x8009588f pledge bit (due to called before pledging, so, no pledging at this point).

    And, when I checked the second process i.e _dhcp, then I got my expected pledge_bit i.e 0x101068 (due to pledging).

    So, I hope things will clear after reading this.

    Note: Please, feel free to update me, if anything I forgot or missed.