Books | Mailing List (marc.info) | Twitter | Github | StackExchange | LinkedIn | About Me

OpenBSD: Introduction to `execpromises` in the pledge(2)

Hi there,

This is the same blog post that I have posted on my medium blog

Today, I would like to introduce you all to the new update on pledge(2) system call.

In my previous post about pledge(2) system call, I have introduced you all about the implementation of pledge(2) system call and under the hood working of pledge(2), that is, the kernel level working of the pledge(2) system call.

In this post I am going to show you about the new updates on the pledge(2) system call and how to use them and why to use them.

On 11 December 2017, Theo de Raadt said:

    List: openbsd-tech
    Subject: pledge execpromises
    From: Theo de Raadt 
    Date: 2017-12-11 21:20:51
    Message-ID: 6735.1513027251 () cvs ! openbsd ! org

    This will probably be committed in the next day or so.

    The 2nd argument of pledge() becomes execpromises, which is what
    will gets activated after execve.

    There is also a small new feature called “error”, which causes
    violating system calls to return -1 with ENOSYS rather than killing
    the process. This must be used with EXTREME CAUTION because libraries
    and programs are full of unchecked system calls. If you carry on past
    one of these failures, your program is in uncharted territory and
    risks of exploitation become high.

    “error” is being introduced for a different reason: The pre-exec
    process’s expectation of what the post-exec process will do might
    mismatch, so “error” allows things like starting an editor which has
    no network access or maybe other restrictions in the future...


OpenBSD 6.2-stable (old) pledge(2):

#include <unistd.h>
int pledge(const char *promises, const char *paths[]);

Now, (updated) pledge(2):

#include <unistd.h>
int pledge(const char *promises, const char *execpromises);

In above updated pledge(2), the second parameter is for child process which is created by using execve() system call.

execpromises in the updated pledge(2) are used to provide promises on child process which is invoked using the combination of execve(2) system call.

execve() system call:

#include <unistd.h>
int execve(const char *file, char *const argv[], char *const envp[]);

Description of exec-family syscalls:

Like all of the exec functions, execve replaces the calling process image with a new process image. This has the effect of running a new program with the process ID of the calling process. Note that a new process is not started; the new process image simply overlays the original process image. The execve function is most commonly used to overlay a process image that has been created by a call to the fork function.

Return value:

A successful call to execve does not have a return value because the new process image overlays the calling process image. However, a -1 is returned if the call to execve is unsuccessful.

Suppose, consider below example as an sample implementation:

#cat test_parent1.c

#include <stdio.h>
#include <unistd.h>

int main (int argc, char **argv)
    if(pledge("stdio exec","stdio rpath") == -1)
        err(1,"parent pledge");

    printf ("Parent: Hello, World!\n");
    char *arg[] = { "./child", 0, 0, 0 };
    execve(arg[0], &arg[0], NULL);
    return 0;

#cat test_child1.c

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
    printf("child process\n");

    while(1) {}
    return 0;

#gcc -o parent test_parent1.c
#gcc -o child test_child1.c

# ./parent &
[1] 80962
# Parent: Hello, World!

# dmesg|grep 80962
process_name:  child    pid:  80962    ps_pledge:  9
process_name:  child    pid:  80962    ps_pledge:  9

As from above code, we have seen that pledge value of the new execve image is 9, that is;

#cat sys/pledge.h

#define PLEDGE_RPATH 0x0000000000000001ULL /* allow open for read */

#define PLEDGE_STDIO 0x0000000000000008ULL /* operate on own pid */

pledge for new execve image:

(RPATH)              (STDIO)                (NEW PLEDGE) 
0x0000000000000001 | 0x0000000000000008 = 0x0000000000000009

So, I think it is better to use fork() then use execve() in the forked process, so that, execve will overlay forked process’s image, not parent’s image.

This update on the pledge(2) system call is the one step further towards improving OS security in OpenBSD.


Share on Facebook | Share on Twitter | Share on LinkedIn