1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "berberis/kernel_api/exec_emulation.h"
18 
19 #include <unistd.h>
20 
21 #include <cstring>
22 #include <utility>
23 
24 #include "berberis/base/mmap.h"
25 #include "berberis/base/strings.h"
26 
27 namespace berberis {
28 
29 namespace {
30 
GetGuestPlatformVarPrefixWithSize()31 std::pair<const char*, size_t> GetGuestPlatformVarPrefixWithSize() {
32   static constexpr char kGuestPlatformVarPrefix[] = "BERBERIS_GUEST_";
33   return {kGuestPlatformVarPrefix, sizeof(kGuestPlatformVarPrefix) - 1};
34 }
35 
IsPlatformVar(const char * s)36 bool IsPlatformVar(const char* s) {
37   return StartsWith(s, "LD_CONFIG_FILE=") || StartsWith(s, "LD_LIBRARY_PATH=") ||
38          StartsWith(s, "LD_DEBUG=") || StartsWith(s, "LD_PRELOAD=");
39 }
40 
MangleGuestEnvp(ScopedMmap * dst,char * const * envp)41 char* const* MangleGuestEnvp(ScopedMmap* dst, char* const* envp) {
42   if (envp == nullptr) {
43     return nullptr;
44   }
45 
46   int env_count = 0;
47   int text_size = 0;
48   int mangle_count = 0;
49 
50   for (;; ++env_count) {
51     char* env = envp[env_count];
52     if (env == nullptr) {
53       break;
54     }
55 
56     if (IsPlatformVar(env)) {
57       ++mangle_count;
58     }
59 
60     text_size += strlen(env) + 1;  // count terminating '\0'
61   }
62 
63   if (mangle_count == 0) {
64     return envp;
65   }
66 
67   auto [guest_prefix, guest_prefix_size] = GetGuestPlatformVarPrefixWithSize();
68 
69   size_t array_size = sizeof(char*) * (env_count + 1);    // text pointers + terminating nullptr
70   dst->Init(array_size + text_size +                      // array + orig text
71             guest_prefix_size * mangle_count);            // added prefixes
72 
73   char** new_array = static_cast<char**>(dst->data());
74   char* new_text = static_cast<char*>(dst->data()) + array_size;
75 
76   for (int i = 0; i < env_count; ++i) {
77     char* env = envp[i];
78     new_array[i] = new_text;
79 
80     if (IsPlatformVar(env)) {
81       strcpy(new_text, guest_prefix);
82       new_text += guest_prefix_size;
83     }
84 
85     strcpy(new_text, env);
86     new_text += strlen(env) + 1;  // count terminating '\0'
87   }
88 
89   new_array[env_count] = nullptr;  // add terminating nullptr
90 
91   return new_array;
92 }
93 
94 }  // namespace
95 
DemangleGuestEnvp(char ** dst,char ** envp)96 char** DemangleGuestEnvp(char** dst, char** envp) {
97   auto [guest_prefix, guest_prefix_size] = GetGuestPlatformVarPrefixWithSize();
98 
99   for (; *envp; ++envp) {
100     char* env = *envp;
101     if (IsPlatformVar(env)) {
102       continue;
103     }
104     if (StartsWith(env, guest_prefix) && IsPlatformVar(env + guest_prefix_size)) {
105       env += guest_prefix_size;
106     }
107     *dst++ = env;
108   }
109 
110   *dst++ = nullptr;
111   return dst;
112 }
113 
ExecveForGuest(const char * filename,char * const argv[],char * const envp[])114 int ExecveForGuest(const char* filename, char* const argv[], char* const envp[]) {
115   ScopedMmap new_envp;
116   return execve(filename, argv, MangleGuestEnvp(&new_envp, envp));
117 }
118 
119 }  // namespace berberis
120