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?
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.