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, <[email protected]> <[email protected]> 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 <stdio.h> > #include <unistd.h> > #include <ultralib.h> > #include <extralib.h> > #include <superlib.h> > #include <mechalib.h> > > 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 <stdio.h> > #include <unistd.h> > #include <ultralib.h> > #include <extralib.h> > #include <superlib.h> > #include <mechalib.h> > > 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?

