2424#include < userver/utils/algo.hpp>
2525#include < utils/check_syscall.hpp>
2626
27+ extern char ** environ;
28+
2729USERVER_NAMESPACE_BEGIN
2830
2931namespace engine ::subprocess {
3032namespace {
3133
32- void DoExecve (const std::string& command, const std::vector<std::string>& args,
33- const EnvironmentVariables& env,
34- const std::optional<std::string>& stdout_file,
35- const std::optional<std::string>& stderr_file) {
34+ void DoExec (const std::string& command, const std::vector<std::string>& args,
35+ const EnvironmentVariables& env,
36+ const std::optional<std::string>& stdout_file,
37+ const std::optional<std::string>& stderr_file, bool use_path ) {
3638 if (stdout_file) {
3739 if (!std::freopen (stdout_file->c_str (), " a" , stdout)) {
3840 utils::CheckSyscall (-1 , " freopen stdout to {}" , *stdout_file);
@@ -65,8 +67,33 @@ void DoExecve(const std::string& command, const std::vector<std::string>& args,
6567 }
6668 envp_ptrs.push_back (nullptr );
6769
68- utils::CheckSyscall (
69- execve (command.c_str (), argv_ptrs.data (), envp_ptrs.data ()), " execve" );
70+ environ = envp_ptrs.data (); // The variable is assigned to the environment to
71+ // use the execv, execvp functions
72+
73+ if (!use_path) {
74+ utils::CheckSyscall (execv (command.c_str (), argv_ptrs.data ()), " execv" );
75+ } else {
76+ utils::CheckSyscall (execvp (command.c_str (), argv_ptrs.data ()), " execvp" );
77+ }
78+ }
79+
80+ EnvironmentVariables ApplyEnviromentUpdate (
81+ std::optional<EnvironmentVariables>&& env,
82+ std::optional<EnvironmentVariablesUpdate>&& env_update) {
83+ if (env) {
84+ if (env_update) {
85+ return env->UpdateWith (std::move (env_update.value ()));
86+ } else {
87+ return std::move (env.value ());
88+ }
89+ } else {
90+ if (env_update) {
91+ return GetCurrentEnvironmentVariables ().UpdateWith (
92+ std::move (env_update.value ()));
93+ } else {
94+ return GetCurrentEnvironmentVariables ();
95+ }
96+ }
7097}
7198
7299} // namespace
@@ -75,11 +102,19 @@ ProcessStarter::ProcessStarter(TaskProcessor& task_processor)
75102 : thread_control_(
76103 task_processor.EventThreadPool().GetEvDefaultLoopThread()) {}
77104
78- ChildProcess ProcessStarter::Exec (
79- const std::string& command, const std::vector<std::string>& args,
80- const EnvironmentVariables& env,
81- const std::optional<std::string>& stdout_file,
82- const std::optional<std::string>& stderr_file) {
105+ ChildProcess ProcessStarter::Exec (const std::string& command,
106+ const std::vector<std::string>& args,
107+ ExecOptions&& options) {
108+ EnvironmentVariables env = ApplyEnviromentUpdate (
109+ std::move (options.env ), std::move (options.env_update ));
110+
111+ if (options.use_path && command.find (' /' ) != std::string::npos &&
112+ !env.GetValueOptional (" PATH" )) {
113+ throw std::runtime_error (
114+ " execvp potential vulnerability. more details "
115+ " https://github.com/userver-framework/userver/issues/588" );
116+ }
117+
83118 tracing::Span span (" ProcessStarter::Exec" );
84119 span.AddTag (" command" , command);
85120 Promise<ChildProcess> promise;
@@ -91,8 +126,9 @@ ChildProcess ProcessStarter::Exec(
91126 return key_value.first + ' =' + key_value.second ;
92127 });
93128 LOG_DEBUG () << fmt::format (
94- " do fork() + execve(), command={}, args=[\' {}\' ], env=[]" ,
95- fmt::join (args, " ' '" ), fmt::join (keys, " , " ));
129+ " do fork() + {}(), command={}, args=[\' {}\' ], env=[]" ,
130+ options.use_path ? " execv" : " execvp" , fmt::join (args, " ' '" ),
131+ fmt::join (keys, " , " ));
96132
97133 const auto pid = utils::CheckSyscall (fork (), " fork" );
98134 if (pid) {
@@ -116,15 +152,16 @@ ChildProcess ProcessStarter::Exec(
116152 // in child thread
117153 try {
118154 try {
119- DoExecve (command, args, env, stdout_file, stderr_file);
155+ DoExec (command, args, env, options.stdout_file , options.stderr_file ,
156+ options.use_path );
120157 } catch (const std::exception& ex) {
121158 std::cerr << " Cannot execute child: " << ex.what ();
122159 }
123160 } catch (...) {
124161 // must not do anything in a child
125162 std::abort ();
126163 }
127- // on success execve does not return
164+ // on success execve or execvp does not return
128165 std::abort ();
129166 }
130167 });
@@ -133,15 +170,24 @@ ChildProcess ProcessStarter::Exec(
133170 return future.get ();
134171}
135172
173+ ChildProcess ProcessStarter::Exec (
174+ const std::string& command, const std::vector<std::string>& args,
175+ const EnvironmentVariables& env,
176+ const std::optional<std::string>& stdout_file,
177+ const std::optional<std::string>& stderr_file) {
178+ ExecOptions options{std::move (env), std::nullopt , std::move (stdout_file),
179+ std::move (stderr_file), false };
180+ return Exec (command, args, std::move (options));
181+ }
182+
136183ChildProcess ProcessStarter::Exec (
137184 const std::string& command, const std::vector<std::string>& args,
138185 EnvironmentVariablesUpdate env_update,
139186 const std::optional<std::string>& stdout_file,
140187 const std::optional<std::string>& stderr_file) {
141- return Exec (command, args,
142- EnvironmentVariables{GetCurrentEnvironmentVariables ()}.UpdateWith (
143- std::move (env_update)),
144- stdout_file, stderr_file);
188+ ExecOptions options{std::nullopt , std::move (env_update),
189+ std::move (stdout_file), std::move (stderr_file), false };
190+ return Exec (command, args, std::move (options));
145191}
146192
147193} // namespace engine::subprocess
0 commit comments