hrev46468 adds 1 changeset to branch 'master' old head: 9f1425e2f49a08243340c465c1eab9cb72cf5ba2 new head: e551626f40c89e7ff5dcac8ce368690fcf382375 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=e551626+%5E9f1425e ---------------------------------------------------------------------------- e551626: Implement support for a SYS:ENV attribute on executable __flatten_process_args() does now have the executable path as an additional (optional) parameter. If specified, the function will read the file's SYS:ENV attribute (if set) and use its value to modified the environment it is preparing for the new process. Currently supported attribute values are strings consisting of "<var>=<value>" substrings separated by "\0" (backslash zero), with '\' being used as an escape character. The environment will be altered to contain the specified "<var>=<value>" elements, replacing a preexisting <var> element (if any). A possible use case would be setting a SYS:ENV attribute with value "DISABLE_ASLR=1" on an executable that needs ASLR disabled. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev46468 Commit: e551626f40c89e7ff5dcac8ce368690fcf382375 URL: http://cgit.haiku-os.org/haiku/commit/?id=e551626 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Dec 1 17:33:42 2013 UTC ---------------------------------------------------------------------------- 7 files changed, 222 insertions(+), 30 deletions(-) headers/private/libroot/libroot_private.h | 4 +- src/bin/debug/debug_utils.cpp | 6 +- src/kits/debug/TeamDebugger.cpp | 6 +- src/kits/tracker/FSUtils.cpp | 13 +- src/kits/tracker/Jamfile | 3 +- src/system/libroot/os/image.cpp | 218 ++++++++++++++++++++++++-- src/system/libroot/posix/unistd/exec.cpp | 2 +- ---------------------------------------------------------------------------- diff --git a/headers/private/libroot/libroot_private.h b/headers/private/libroot/libroot_private.h index 85bc14d1..7f6dcd5 100644 --- a/headers/private/libroot/libroot_private.h +++ b/headers/private/libroot/libroot_private.h @@ -29,8 +29,8 @@ status_t __get_next_image_dependency(image_id id, uint32 *cookie, const char **_name); status_t __test_executable(const char *path, char *invoker); status_t __flatten_process_args(const char* const* args, int32 argCount, - const char* const* env, int32 envCount, char*** _flatArgs, - size_t* _flatSize); + const char* const* env, int32* envCount, const char* executablePath, + char*** _flatArgs, size_t* _flatSize); void _call_atexit_hooks_for_range(addr_t start, addr_t size); void __init_env(const struct user_space_program_args *args); status_t __init_heap(void); diff --git a/src/bin/debug/debug_utils.cpp b/src/bin/debug/debug_utils.cpp index e91bf36..4bfc361 100644 --- a/src/bin/debug/debug_utils.cpp +++ b/src/bin/debug/debug_utils.cpp @@ -90,15 +90,15 @@ load_program(const char* const* args, int32 argCount, bool traceLoading) mutableArgs[0] = programPath.c_str(); // count environment variables - int envCount = 0; + int32 envCount = 0; while (environ[envCount] != NULL) envCount++; // flatten the program args and environment char** flatArgs = NULL; size_t flatArgsSize; - error = __flatten_process_args(mutableArgs, argCount, environ, envCount, - &flatArgs, &flatArgsSize); + error = __flatten_process_args(mutableArgs, argCount, environ, &envCount, + mutableArgs[0], &flatArgs, &flatArgsSize); // load the program thread_id thread; diff --git a/src/kits/debug/TeamDebugger.cpp b/src/kits/debug/TeamDebugger.cpp index cb92511..eb0abe6 100644 --- a/src/kits/debug/TeamDebugger.cpp +++ b/src/kits/debug/TeamDebugger.cpp @@ -134,15 +134,15 @@ BTeamDebugger::_LoadProgram(const char* const* args, int32 argCount, mutableArgs[0] = programPath.Path(); // count environment variables - int envCount = 0; + int32 envCount = 0; while (environ[envCount] != NULL) envCount++; // flatten the program args and environment char** flatArgs = NULL; size_t flatArgsSize; - error = __flatten_process_args(mutableArgs, argCount, environ, envCount, - &flatArgs, &flatArgsSize); + error = __flatten_process_args(mutableArgs, argCount, environ, &envCount, + mutableArgs[0], &flatArgs, &flatArgsSize); // load the program thread_id thread; diff --git a/src/kits/tracker/FSUtils.cpp b/src/kits/tracker/FSUtils.cpp index 790d286..63eda73 100644 --- a/src/kits/tracker/FSUtils.cpp +++ b/src/kits/tracker/FSUtils.cpp @@ -72,6 +72,8 @@ respective holders. All rights reserved. #include <sys/utsname.h> #include <AutoLocker.h> +#include <libroot/libroot_private.h> +#include <system/syscalls.h> #include "Attributes.h" #include "Bitmaps.h" @@ -3334,13 +3336,6 @@ _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs, extern "C" char** environ; -extern "C" status_t _kern_load_image(const char* const* flatArgs, - size_t flatArgsSize, int32 argCount, int32 envCount, int32 priority, - uint32 flags, port_id errorPort, uint32 errorToken); -extern "C" status_t __flatten_process_args(const char* const* args, - int32 argCount, const char* const* env, int32 envCount, char***_flatArgs, - size_t* _flatSize); - static status_t LoaderErrorDetails(const entry_ref* app, BString &details) @@ -3357,14 +3352,14 @@ LoaderErrorDetails(const entry_ref* app, BString &details) port_id errorPort = create_port(1, "Tracker loader error"); // count environment variables - uint32 envCount = 0; + int32 envCount = 0; while (environ[envCount] != NULL) envCount++; char** flatArgs = NULL; size_t flatArgsSize; result = __flatten_process_args((const char**)argv, 1, - environ, envCount, &flatArgs, &flatArgsSize); + environ, &envCount, argv[0], &flatArgs, &flatArgsSize); if (result != B_OK) return result; diff --git a/src/kits/tracker/Jamfile b/src/kits/tracker/Jamfile index 45d6c4b..66a90ea 100644 --- a/src/kits/tracker/Jamfile +++ b/src/kits/tracker/Jamfile @@ -3,7 +3,8 @@ SubDir HAIKU_TOP src kits tracker ; SetSubDirSupportedPlatformsBeOSCompatible ; AddSubDirSupportedPlatforms libbe_test ; -UsePrivateHeaders interface mount shared storage support system tracker ; +UsePrivateHeaders interface mount shared storage support tracker ; +UsePrivateSystemHeaders ; SubDirC++Flags -D_BUILDING_tracker=1 diff --git a/src/system/libroot/os/image.cpp b/src/system/libroot/os/image.cpp index e338469..9f76a4a 100644 --- a/src/system/libroot/os/image.cpp +++ b/src/system/libroot/os/image.cpp @@ -9,7 +9,12 @@ #include <stdlib.h> #include <string.h> -#include <OS.h> +#include <algorithm> +#include <new> + +#include <fs_attr.h> + +#include <AutoDeleter.h> #include <libroot_private.h> #include <runtime_loader.h> @@ -17,6 +22,183 @@ #include <user_runtime.h> +struct EnvironmentFilter { + EnvironmentFilter() + : + fBuffer(NULL), + fEntries(NULL), + fBufferSize(0), + fEntryCount(0), + fAdditionalEnvCount(0), + fNextEntryIndex(0) + { + } + + ~EnvironmentFilter() + { + free(fBuffer); + delete[] fEntries; + } + + void Init(const char* path, const char* const* env, size_t envCount) + { + int fd = open(path, O_RDONLY); + if (fd < 0) + return; + FileDescriptorCloser fdCloser(fd); + + static const char* const kEnvAttribute = "SYS:ENV"; + attr_info info; + if (fs_stat_attr(fd, kEnvAttribute, &info) < 0) + return; + + _Init(fd, kEnvAttribute, info.size, env, envCount); + } + + size_t AdditionalSlotsNeeded() const + { + return fAdditionalEnvCount; + } + + size_t AdditionalSizeNeeded() const + { + return fBufferSize + fAdditionalEnvCount * sizeof(char*); + } + + size_t PrepareSlot(const char* env, int32 index, char* buffer) + { + if (fNextEntryIndex < fEntryCount + && fEntries[fNextEntryIndex].index == index) { + env = fEntries[fNextEntryIndex].replacement; + fNextEntryIndex++; + } + + return _FillSlot(env, buffer); + } + + void PrepareAdditionalSlots(char**& slot, char*& buffer) + { + for (size_t i = 0; i < fAdditionalEnvCount; i++) { + size_t envSize = _FillSlot(fEntries[i].replacement, buffer); + *slot++ = buffer; + buffer += envSize; + } + } + +private: + void _Init(int fd, const char* attribute, size_t size, + const char* const* env, size_t envCount) + { + if (size == 0) + return; + + // read the attribute + char* buffer = (char*)malloc(size + 1); + if (buffer == NULL) + return; + MemoryDeleter bufferDeleter(buffer); + + ssize_t bytesRead = fs_read_attr(fd, attribute, B_STRING_TYPE, 0, + buffer, size); + if (bytesRead < 0 || (size_t)bytesRead != size) + return; + buffer[size] = '\0'; + + // deescape the buffer and count the entries + size_t entryCount = 1; + char* out = buffer; + for (const char* c = buffer; *c != '\0'; c++) { + if (*c == '\\') { + c++; + if (*c == '\0') + break; + if (*c == '0') { + *out++ = '\0'; + entryCount++; + } else + *out++ = *c; + } else + *out++ = *c; + } + *out++ = '\0'; + size = out - buffer + 1; + + // create an entry array + fEntries = new(std::nothrow) Entry[entryCount]; + if (fEntries == NULL) + return; + + bufferDeleter.Detach(); + fBuffer = buffer; + fBufferSize = size; + + // init the entries + out = buffer; + for (size_t i = 0; i < entryCount; i++) { + const char* separator = strchr(out, '='); + if (separator != NULL && separator != out) { + fEntries[fEntryCount].replacement = out; + fEntries[fEntryCount].index = _FindEnvEntry(env, envCount, out, + separator - out); + if (fEntries[fEntryCount].index < 0) + fAdditionalEnvCount++; + fEntryCount++; + } + out += strlen(out) + 1; + } + + if (fEntryCount > 1) + std::sort(fEntries, fEntries + fEntryCount); + + // Advance fNextEntryIndex to the first entry pointing to an existing + // env variable. + while (fNextEntryIndex < fEntryCount + && fEntries[fNextEntryIndex].index < 0) { + fNextEntryIndex++; + } + } + + int32 _FindEnvEntry(const char* const* env, size_t envCount, + const char* variable, size_t variableLength) + { + for (size_t i = 0; i < envCount; i++) { + if (strncmp(env[i], variable, variableLength) == 0 + && env[i][variableLength] == '=') { + return i; + } + } + + return -1; + } + + size_t _FillSlot(const char* env, char* buffer) + { + size_t envSize = strlen(env) + 1; + memcpy(buffer, env, envSize); + return envSize; + } + +private: + struct Entry { + char* replacement; + int32 index; + + bool operator<(const Entry& other) const + { + return index < other.index; + } + }; + +private: + char* fBuffer; + Entry* fEntries; + size_t fBufferSize; + size_t fEntryCount; + size_t fAdditionalEnvCount; + size_t fNextEntryIndex; +}; + + thread_id load_image(int32 argCount, const char **args, const char **environ) { @@ -48,8 +230,8 @@ load_image(int32 argCount, const char **args, const char **environ) char** flatArgs = NULL; size_t flatArgsSize; - status_t status = __flatten_process_args(args, argCount, environ, envCount, - &flatArgs, &flatArgsSize); + status_t status = __flatten_process_args(args, argCount, environ, + &envCount, args[0], &flatArgs, &flatArgsSize); if (status == B_OK) { thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount, @@ -230,15 +412,20 @@ __test_executable(const char *path, char *invoker) into it. The buffer starts with a char* array which contains pointers to the strings of the arguments and environment, followed by the strings. Both arguments and environment arrays are NULL-terminated. + If executablePath is non-NULL, it should refer to the executable to be + executed. If the executable file specifies changes to environment variable + values, those will be performed. */ status_t __flatten_process_args(const char* const* args, int32 argCount, - const char* const* env, int32 envCount, char*** _flatArgs, - size_t* _flatSize) + const char* const* env, int32* _envCount, const char* executablePath, + char*** _flatArgs, size_t* _flatSize) { - if (args == NULL || env == NULL) + if (args == NULL || env == NULL || _envCount == NULL) return B_BAD_VALUE; + int32 envCount = *_envCount; + // determine total needed size int32 argSize = 0; for (int32 i = 0; i < argCount; i++) { @@ -254,7 +441,14 @@ __flatten_process_args(const char* const* args, int32 argCount, envSize += strlen(env[i]) + 1; } - int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize; + EnvironmentFilter envFilter; + if (executablePath != NULL) + envFilter.Init(executablePath, env, envCount); + + int32 totalSlotCount = argCount + envCount + 2 + + envFilter.AdditionalSlotsNeeded(); + int32 size = totalSlotCount * sizeof(char*) + argSize + envSize + + envFilter.AdditionalSizeNeeded(); if (size > MAX_PROCESS_ARGS_SIZE) return B_TOO_MANY_ARGS; @@ -264,7 +458,7 @@ __flatten_process_args(const char* const* args, int32 argCount, return B_NO_MEMORY; char** slot = flatArgs; - char* stringSpace = (char*)(flatArgs + argCount + envCount + 2); + char* stringSpace = (char*)(flatArgs + totalSlotCount); // copy arguments and environment for (int32 i = 0; i < argCount; i++) { @@ -277,16 +471,18 @@ __flatten_process_args(const char* const* args, int32 argCount, *slot++ = NULL; for (int32 i = 0; i < envCount; i++) { - int32 envSize = strlen(env[i]) + 1; - memcpy(stringSpace, env[i], envSize); + size_t envSize = envFilter.PrepareSlot(env[i], i, stringSpace); *slot++ = stringSpace; stringSpace += envSize; } + envFilter.PrepareAdditionalSlots(slot, stringSpace); + *slot++ = NULL; + *_envCount = envCount + envFilter.AdditionalSlotsNeeded(); *_flatArgs = flatArgs; - *_flatSize = size; + *_flatSize = stringSpace - (char*)flatArgs; return B_OK; } diff --git a/src/system/libroot/posix/unistd/exec.cpp b/src/system/libroot/posix/unistd/exec.cpp index 7c3e5d1..4f3b2a3 100644 --- a/src/system/libroot/posix/unistd/exec.cpp +++ b/src/system/libroot/posix/unistd/exec.cpp @@ -103,7 +103,7 @@ do_exec(const char *path, char * const args[], char * const environment[], char** flatArgs = NULL; size_t flatArgsSize; status = __flatten_process_args(newArgs ? newArgs : args, argCount, - environment, envCount, &flatArgs, &flatArgsSize); + environment, &envCount, path, &flatArgs, &flatArgsSize); if (status == B_OK) { __set_errno(_kern_exec(path, flatArgs, flatArgsSize, argCount, envCount,