tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

system_utils_posix.cpp (11683B)


      1 //
      2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 
      7 // system_utils_posix.cpp: Implementation of POSIX OS-specific functions.
      8 
      9 #include "common/debug.h"
     10 #include "system_utils.h"
     11 
     12 #include <array>
     13 #include <iostream>
     14 
     15 #include <dlfcn.h>
     16 #include <grp.h>
     17 #include <inttypes.h>
     18 #include <pwd.h>
     19 #include <signal.h>
     20 #include <string.h>
     21 #include <sys/mman.h>
     22 #include <sys/stat.h>
     23 #include <sys/types.h>
     24 #include <sys/wait.h>
     25 #include <unistd.h>
     26 
     27 #include "common/string_utils.h"
     28 
     29 #ifdef ANGLE_PLATFORM_FUCHSIA
     30 #    include <zircon/process.h>
     31 #    include <zircon/syscalls.h>
     32 #else
     33 #    include <sys/resource.h>
     34 #endif
     35 
     36 namespace angle
     37 {
     38 
     39 namespace
     40 {
     41 std::string GetModulePath(void *moduleOrSymbol)
     42 {
     43    Dl_info dlInfo;
     44    if (dladdr(moduleOrSymbol, &dlInfo) == 0)
     45    {
     46        return "";
     47    }
     48 
     49    return dlInfo.dli_fname;
     50 }
     51 
     52 void *OpenPosixLibrary(const std::string &fullPath, int extraFlags, std::string *errorOut)
     53 {
     54    void *module = dlopen(fullPath.c_str(), RTLD_NOW | extraFlags);
     55    if (module)
     56    {
     57        if (errorOut)
     58        {
     59            *errorOut = fullPath;
     60        }
     61    }
     62    else if (errorOut)
     63    {
     64        *errorOut = "dlopen(";
     65        *errorOut += fullPath;
     66        *errorOut += ") failed with error: ";
     67        *errorOut += dlerror();
     68        struct stat sfile;
     69        if (-1 == stat(fullPath.c_str(), &sfile))
     70        {
     71            *errorOut += ", stat() call failed.";
     72        }
     73        else
     74        {
     75            *errorOut += ", stat() info: ";
     76            struct passwd *pwuser = getpwuid(sfile.st_uid);
     77            if (pwuser)
     78            {
     79                *errorOut += "owner: ";
     80                *errorOut += pwuser->pw_name;
     81                *errorOut += ", ";
     82            }
     83            struct group *grpnam = getgrgid(sfile.st_gid);
     84            if (grpnam)
     85            {
     86                *errorOut += "group: ";
     87                *errorOut += grpnam->gr_name;
     88                *errorOut += ", ";
     89            }
     90            *errorOut += "perms: ";
     91            *errorOut += std::to_string(sfile.st_mode);
     92            *errorOut += ", links: ";
     93            *errorOut += std::to_string(sfile.st_nlink);
     94            *errorOut += ", size: ";
     95            *errorOut += std::to_string(sfile.st_size);
     96        }
     97    }
     98    return module;
     99 }
    100 }  // namespace
    101 
    102 Optional<std::string> GetCWD()
    103 {
    104    std::array<char, 4096> pathBuf;
    105    char *result = getcwd(pathBuf.data(), pathBuf.size());
    106    if (result == nullptr)
    107    {
    108        return Optional<std::string>::Invalid();
    109    }
    110    return std::string(pathBuf.data());
    111 }
    112 
    113 bool SetCWD(const char *dirName)
    114 {
    115    return (chdir(dirName) == 0);
    116 }
    117 
    118 bool UnsetEnvironmentVar(const char *variableName)
    119 {
    120    return (unsetenv(variableName) == 0);
    121 }
    122 
    123 bool SetEnvironmentVar(const char *variableName, const char *value)
    124 {
    125    return (setenv(variableName, value, 1) == 0);
    126 }
    127 
    128 std::string GetEnvironmentVar(const char *variableName)
    129 {
    130    const char *value = getenv(variableName);
    131    return (value == nullptr ? std::string() : std::string(value));
    132 }
    133 
    134 const char *GetPathSeparatorForEnvironmentVar()
    135 {
    136    return ":";
    137 }
    138 
    139 std::string GetModuleDirectoryAndGetError(std::string *errorOut)
    140 {
    141    std::string directory;
    142    static int placeholderSymbol = 0;
    143    std::string moduleName       = GetModulePath(&placeholderSymbol);
    144    if (!moduleName.empty())
    145    {
    146        directory = moduleName.substr(0, moduleName.find_last_of('/') + 1);
    147    }
    148 
    149    // Ensure we return the full path to the module, not the relative path
    150    if (!IsFullPath(directory))
    151    {
    152        if (errorOut)
    153        {
    154            *errorOut += "Directory: '";
    155            *errorOut += directory;
    156            *errorOut += "' is not full path";
    157        }
    158        Optional<std::string> cwd = GetCWD();
    159        if (cwd.valid())
    160        {
    161            directory = ConcatenatePath(cwd.value(), directory);
    162            if (errorOut)
    163            {
    164                *errorOut += ", so it has been modified to: '";
    165                *errorOut += directory;
    166                *errorOut += "'. ";
    167            }
    168        }
    169        else if (errorOut)
    170        {
    171            *errorOut += " and getcwd was invalid. ";
    172        }
    173    }
    174    return directory;
    175 }
    176 
    177 std::string GetModuleDirectory()
    178 {
    179    return GetModuleDirectoryAndGetError(nullptr);
    180 }
    181 
    182 void *OpenSystemLibraryWithExtensionAndGetError(const char *libraryName,
    183                                                SearchType searchType,
    184                                                std::string *errorOut)
    185 {
    186    std::string directory;
    187    if (searchType == SearchType::ModuleDir)
    188    {
    189 #if ANGLE_PLATFORM_IOS
    190        // On iOS, shared libraries must be loaded from within the app bundle.
    191        directory = GetExecutableDirectory() + "/Frameworks/";
    192 #elif ANGLE_PLATFORM_FUCHSIA
    193        // On Fuchsia the dynamic loader always looks up libraries in /pkg/lib
    194        // and disallows loading of libraries via absolute paths.
    195        directory = "";
    196 #else
    197        directory = GetModuleDirectoryAndGetError(errorOut);
    198 #endif
    199    }
    200 
    201    int extraFlags = 0;
    202    if (searchType == SearchType::AlreadyLoaded)
    203    {
    204        extraFlags = RTLD_NOLOAD;
    205    }
    206 
    207    std::string fullPath = directory + libraryName;
    208    return OpenPosixLibrary(fullPath, extraFlags, errorOut);
    209 }
    210 
    211 void *GetLibrarySymbol(void *libraryHandle, const char *symbolName)
    212 {
    213    if (!libraryHandle)
    214    {
    215        return nullptr;
    216    }
    217 
    218    return dlsym(libraryHandle, symbolName);
    219 }
    220 
    221 std::string GetLibraryPath(void *libraryHandle)
    222 {
    223    if (!libraryHandle)
    224    {
    225        return "";
    226    }
    227 
    228    return GetModulePath(libraryHandle);
    229 }
    230 
    231 void CloseSystemLibrary(void *libraryHandle)
    232 {
    233    if (libraryHandle)
    234    {
    235        dlclose(libraryHandle);
    236    }
    237 }
    238 
    239 bool IsDirectory(const char *filename)
    240 {
    241    struct stat st;
    242    int result = stat(filename, &st);
    243    return result == 0 && ((st.st_mode & S_IFDIR) == S_IFDIR);
    244 }
    245 
    246 bool IsDebuggerAttached()
    247 {
    248    // This could have a fuller implementation.
    249    // See https://cs.chromium.org/chromium/src/base/debug/debugger_posix.cc
    250    return false;
    251 }
    252 
    253 void BreakDebugger()
    254 {
    255    // This could have a fuller implementation.
    256    // See https://cs.chromium.org/chromium/src/base/debug/debugger_posix.cc
    257    abort();
    258 }
    259 
    260 const char *GetExecutableExtension()
    261 {
    262    return "";
    263 }
    264 
    265 char GetPathSeparator()
    266 {
    267    return '/';
    268 }
    269 
    270 std::string GetRootDirectory()
    271 {
    272    return "/";
    273 }
    274 
    275 Optional<std::string> GetTempDirectory()
    276 {
    277    const char *tmp = getenv("TMPDIR");
    278    if (tmp != nullptr)
    279    {
    280        return std::string(tmp);
    281    }
    282 
    283 #if defined(ANGLE_PLATFORM_ANDROID)
    284    // Not used right now in the ANGLE test runner.
    285    // return PathService::Get(DIR_CACHE, path);
    286    return Optional<std::string>::Invalid();
    287 #else
    288    return std::string("/tmp");
    289 #endif
    290 }
    291 
    292 Optional<std::string> CreateTemporaryFileInDirectory(const std::string &directory)
    293 {
    294    std::string tempFileTemplate = directory + "/.angle.XXXXXX";
    295 
    296    char tempFile[1000];
    297    strcpy(tempFile, tempFileTemplate.c_str());
    298 
    299    int fd = mkstemp(tempFile);
    300    close(fd);
    301 
    302    if (fd != -1)
    303    {
    304        return std::string(tempFile);
    305    }
    306 
    307    return Optional<std::string>::Invalid();
    308 }
    309 
    310 double GetCurrentProcessCpuTime()
    311 {
    312 #ifdef ANGLE_PLATFORM_FUCHSIA
    313    static zx_handle_t me = zx_process_self();
    314    zx_info_task_runtime_t task_runtime;
    315    zx_object_get_info(me, ZX_INFO_TASK_RUNTIME, &task_runtime, sizeof(task_runtime), nullptr,
    316                       nullptr);
    317    return static_cast<double>(task_runtime.cpu_time) * 1e-9;
    318 #else
    319    // We could also have used /proc/stat, but that requires us to read the
    320    // filesystem and convert from jiffies. /proc/stat also relies on jiffies
    321    // (lower resolution) while getrusage can potentially use a sched_clock()
    322    // underneath that has higher resolution.
    323    struct rusage usage;
    324    getrusage(RUSAGE_SELF, &usage);
    325    double userTime = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec * 1e-6;
    326    double systemTime = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec * 1e-6;
    327    return userTime + systemTime;
    328 #endif
    329 }
    330 
    331 namespace
    332 {
    333 bool SetMemoryProtection(uintptr_t start, size_t size, int protections)
    334 {
    335    int ret = mprotect(reinterpret_cast<void *>(start), size, protections);
    336    if (ret < 0)
    337    {
    338        perror("mprotect failed");
    339    }
    340    return ret == 0;
    341 }
    342 
    343 class PosixPageFaultHandler : public PageFaultHandler
    344 {
    345  public:
    346    PosixPageFaultHandler(PageFaultCallback callback) : PageFaultHandler(callback) {}
    347    ~PosixPageFaultHandler() override {}
    348 
    349    bool enable() override;
    350    bool disable() override;
    351    void handle(int sig, siginfo_t *info, void *unused);
    352 
    353  private:
    354    struct sigaction mDefaultBusAction  = {};
    355    struct sigaction mDefaultSegvAction = {};
    356 };
    357 
    358 PosixPageFaultHandler *gPosixPageFaultHandler = nullptr;
    359 void SegfaultHandlerFunction(int sig, siginfo_t *info, void *unused)
    360 {
    361    gPosixPageFaultHandler->handle(sig, info, unused);
    362 }
    363 
    364 void PosixPageFaultHandler::handle(int sig, siginfo_t *info, void *unused)
    365 {
    366    bool found = false;
    367    if ((sig == SIGSEGV || sig == SIGBUS) &&
    368        (info->si_code == SEGV_ACCERR || info->si_code == SEGV_MAPERR))
    369    {
    370        found = mCallback(reinterpret_cast<uintptr_t>(info->si_addr)) ==
    371                PageFaultHandlerRangeType::InRange;
    372    }
    373 
    374    // Fall back to default signal handler
    375    if (!found)
    376    {
    377        if (sig == SIGSEGV)
    378        {
    379            mDefaultSegvAction.sa_sigaction(sig, info, unused);
    380        }
    381        else if (sig == SIGBUS)
    382        {
    383            mDefaultBusAction.sa_sigaction(sig, info, unused);
    384        }
    385        else
    386        {
    387            UNREACHABLE();
    388        }
    389    }
    390 }
    391 
    392 bool PosixPageFaultHandler::disable()
    393 {
    394    return sigaction(SIGSEGV, &mDefaultSegvAction, nullptr) == 0 &&
    395           sigaction(SIGBUS, &mDefaultBusAction, nullptr) == 0;
    396 }
    397 
    398 bool PosixPageFaultHandler::enable()
    399 {
    400    struct sigaction sigAction = {};
    401    sigAction.sa_flags         = SA_SIGINFO;
    402    sigAction.sa_sigaction     = &SegfaultHandlerFunction;
    403    sigemptyset(&sigAction.sa_mask);
    404 
    405    // Some POSIX implementations use SIGBUS for mprotect faults
    406    return sigaction(SIGSEGV, &sigAction, &mDefaultSegvAction) == 0 &&
    407           sigaction(SIGBUS, &sigAction, &mDefaultBusAction) == 0;
    408 }
    409 }  // namespace
    410 
    411 // Set write protection
    412 bool ProtectMemory(uintptr_t start, size_t size)
    413 {
    414    return SetMemoryProtection(start, size, PROT_READ);
    415 }
    416 
    417 // Allow reading and writing
    418 bool UnprotectMemory(uintptr_t start, size_t size)
    419 {
    420    return SetMemoryProtection(start, size, PROT_READ | PROT_WRITE);
    421 }
    422 
    423 size_t GetPageSize()
    424 {
    425    long pageSize = sysconf(_SC_PAGE_SIZE);
    426    if (pageSize < 0)
    427    {
    428        perror("Could not get sysconf page size");
    429        return 0;
    430    }
    431    return static_cast<size_t>(pageSize);
    432 }
    433 
    434 PageFaultHandler *CreatePageFaultHandler(PageFaultCallback callback)
    435 {
    436    gPosixPageFaultHandler = new PosixPageFaultHandler(callback);
    437    return gPosixPageFaultHandler;
    438 }
    439 
    440 uint64_t GetProcessMemoryUsageKB()
    441 {
    442    FILE *file = fopen("/proc/self/status", "r");
    443 
    444    if (!file)
    445    {
    446        return 0;
    447    }
    448 
    449    const char *kSearchString           = "VmRSS:";
    450    constexpr size_t kMaxLineSize       = 100;
    451    std::array<char, kMaxLineSize> line = {};
    452 
    453    uint64_t kb = 0;
    454 
    455    while (fgets(line.data(), line.size(), file) != nullptr)
    456    {
    457        if (strncmp(line.data(), kSearchString, strlen(kSearchString)) == 0)
    458        {
    459            std::vector<std::string> strings;
    460            SplitStringAlongWhitespace(line.data(), &strings);
    461 
    462            sscanf(strings[1].c_str(), "%" SCNu64, &kb);
    463            break;
    464        }
    465    }
    466    fclose(file);
    467 
    468    return kb;
    469 }
    470 }  // namespace angle