diff --git a/lib/libutil/getlocalbase.3 b/lib/libutil/getlocalbase.3 index ed0a4a3c1226..50466123ee89 100644 --- a/lib/libutil/getlocalbase.3 +++ b/lib/libutil/getlocalbase.3 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 18, 2020 +.Dd November 25, 2020 .Dt GETLOCALBASE 3 .Os .Sh NAME @@ -59,7 +59,7 @@ If that is undefined then the default of .Pa /usr/local is used. .Pp -The value returned by the +The contents of the string returned by the .Fn getlocalbase function shall not be modified. .Sh IMPLEMENTATION NOTES @@ -67,13 +67,34 @@ Calls to .Fn getlocalbase will perform a setugid check on the running binary before checking the environment. +.Pp +The address returned by +.Fn getlocalbase +will point into the executing processes environment if it is the result of +.Fn getenv "LOCALBASE" , +to a static buffer if it is the result of +.Fn sysctl "user.localbase" , +and to a constant string if the compiled in default value is returned. +.Pp +The same value will be returned on successive calls during the run-time +of the program, ignoring any changes to the environment variable or the +sysctl value that might have been made. +.Pp +The +.Fn getlocalbase +function can be compiled with a non-default value of LOCALBASE_CTL_LEN. +A value of 0 will disable fetching of the sysctl value, a value less than +MAXPATHLEN will put a limit on the maximum string length supported for +this sysctl value. +If built with a non-default value of LOCALBASE_CTL_LEN, a value of the +user.localbase sysctl variable longer than this value will make +.Fn getlocalbase +return a valid string that is not a valid path prefix in any filesystem. .Sh RETURN VALUES The .Fn getlocalbase -function always succeeds and returns a pointer to a string, whose length -may exceed MAXPATHLEN if it has been derived from the environment variable -LOCALBASE. -No length checks are performed on the result. +function returns a pointer to a string, whose length may exceed MAXPATHLEN, +if it has been obtained from the environment. .Sh ENVIRONMENT The .Fn getlocalbase @@ -83,7 +104,7 @@ environment variable. .Sh ERRORS The .Fn getlocalbase -function always succeeds. +function always succeeds and returns a valid pointer to a string. .Sh SEE ALSO .Xr env 1 , .Xr src.conf 5 , diff --git a/lib/libutil/getlocalbase.c b/lib/libutil/getlocalbase.c index 3d6bcc067391..8ecd8447155c 100644 --- a/lib/libutil/getlocalbase.c +++ b/lib/libutil/getlocalbase.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -40,35 +41,50 @@ __FBSDID("$FreeBSD$"); #define _PATH_LOCALBASE "/usr/local" #endif +#ifndef LOCALBASE_CTL_LEN +#define LOCALBASE_CTL_LEN MAXPATHLEN +#endif + +/* Any prefix guaranteed to not be the start of a valid path name */ +#define ILLEGAL_PREFIX "/dev/null/" + const char * getlocalbase(void) { - static const int localbase_oid[2] = {CTL_USER, USER_LOCALBASE}; +#if LOCALBASE_CTL_LEN > 0 + int localbase_oid[2] = {CTL_USER, USER_LOCALBASE}; + static char localpath[LOCALBASE_CTL_LEN]; + size_t localpathlen = LOCALBASE_CTL_LEN; +#endif char *tmppath; - size_t tmplen; static const char *localbase = NULL; + if (localbase != NULL) + return (localbase); + if (issetugid() == 0) { tmppath = getenv("LOCALBASE"); - if (tmppath != NULL && tmppath[0] != '\0') - return (tmppath); - } - if (sysctl(localbase_oid, 2, NULL, &tmplen, NULL, 0) == 0 && - (tmppath = malloc(tmplen)) != NULL && - sysctl(localbase_oid, 2, tmppath, &tmplen, NULL, 0) == 0) { - /* - * Check for some other thread already having - * set localbase - this should use atomic ops. - * The amount of memory allocated above may leak, - * if a parallel update in another thread is not - * detected and the non-NULL pointer is overwritten. - */ - if (tmppath[0] != '\0' && - (volatile const char*)localbase == NULL) + if (tmppath != NULL && tmppath[0] != '\0') { localbase = tmppath; - else - free((void*)tmppath); - return (localbase); + return (localbase); + } } - return (_PATH_LOCALBASE); + +#if LOCALBASE_CTL_LEN > 0 + if (sysctl(localbase_oid, 2, localpath, &localpathlen, NULL, 0) != 0) { + if (errno != ENOMEM) + localbase = _PATH_LOCALBASE; + else + localbase = ILLEGAL_PREFIX; + } else { + if (localpath[0] != '\0') + localbase = localpath; + else + localbase = _PATH_LOCALBASE; + } +#else + localbase = _PATH_LOCALBASE; +#endif + + return (localbase); }