pledge should be used to restrict a program to whatever it is necessary to do,
rather than everything the library can do. So if I use libimaginarydb to parse
a csv file I've already read into a memory buffer (nearly pledge("", NULL)),
but the library can read/write/create files, do remote db connections, and
fork shells, we don't want it to do all that (pledge("rpath wpath cpath dns
inet exec proc", NULL)).
> On Jun 13, 2016, at 12:56 PM,
wrote:
>
> I have thought of a way pledge(2) can be made a little more
> library-friendly.
>
> This is not a patch, but just a thought.
> There are 2 setups I have thought of:
>
> === 1. Variable arguments ===
>
> int pledge(const char *promises, const char *paths[])
> {
>return vpledge(1, promises, paths);
> }
>
> int vpledge(const size_t npledge, ...);
>
> -
>
> In a program, this may be something like this:
>
> #include
> #include
> #include
> #include
> #include
> #include
>
> int main(void) {
>if(vpledge(5, "stdio rpath wpath cpath", NULL,
>ultra_promises, ultra_pledgepaths,
>extra_promises, NULL, super_promises, NULL,
>mecha_promises, mecha_pledgepaths) == -1)
>errx("pledge");
>
>... [other code] ...
> };
>
>
> ---
>
> In vpledge(), "npledge" refers to the number of pledge-pairs, which
> consist of:
>
>const char *promises, const char *paths[]
>
> These have the same semantics as the original pledge().
>
> A library can export *_promises and *_pledgepaths symbols, pointing to
> static text. This allows the library to change without the program
> being affected because the new library changes call something outside
> the original pledge() of a program.
>
> = 2. Using a struct ==
>
> -
>
> struct pledge {
>char *promises;
>char *paths[];
> };
>
> -
>
> int pledge(const char *promises, const char *paths[])
> {
>struct pledge pl = {
>.promises, paths
>};
>
>return pledges(1, &pl);
> }
>
> int pledges(const size_t npledge, const struct pledge pledge_array[]);
>
> -
>
> In a program, this may be something like this:
>
> #include
> #include
> #include
> #include
> #include
> #include
>
> int main(void) {
>struct pledge pl[4];
>
>pl[0].promises = "stdio rpath wpath cpath";
>ultra_getpledge(&pl[1]);
>extra_getpledge(&pl[2]);
>super_getpledge(&pl[3]);
>mecha_getpledge(&pl[4]);
>
>if(pledges(5, pl) == -1)
>errx("pledge");
>
>... [other code] ...
>
>
> };
>
>
> ---
>
>
> A library can tell the application what pledges are in use as follows:
>
>
> static const char *pledge_promises = "stdio fattr sendfd recvfd"
>
> void ultra_getpledge(struct pledge *const pl)
> {
>pl->promises = pledge_promises;
>pl->paths = NULL;
> }
>
>
> ==
>
> I think that #1 has the advantage of it being easier to code so a
> program can ratchet down its abilities. #2 allows one to group the
> pledge arguments into a single struct.
>
> Thoughts?