2
\$\begingroup\$

I'm working on a fork of Cygwin for native Windows development.

In this commit, I'm trying to improve the security of code that spawns cmd.exe, used by functions like system, popen.

What I would like to focus on is this:

static const char *cmd_exe_path = NULL;

static void init_cmd_exe_path(void)
{
  char sysdir[NT_MAX_PATH];
  char cmdname[] = "cmd.exe";
  unsigned int nchars;

  if ((nchars = GetSystemDirectoryA(sysdir, sizeof sysdir)) < sizeof sysdir) {
    unsigned int total = nchars + 1 + sizeof cmdname + 1;
    char *path = static_cast<char *>(cmalloc_abort(HEAP_STR, total));
    snprintf(path, total, "%s\\%s", sysdir, cmdname);
    cmd_exe_path = path;
  }
}

const char *get_cmd_exe_path()
{
  static pthread_once_t cmd_exe_once = PTHREAD_ONCE_INIT;
  pthread_once (&cmd_exe_once, init_cmd_exe_path);
  return cmd_exe_path;
}

The idea here is to avoid relying on a hard-coded path like C:\Windows\System32\cmd.exe, as well as to avoid the COMSPEC environment variable which could be commandeered by an attacker. Comment about anything are welcome: API usage, bugs, security concerns, correct use of Cygwin internals (I have no idea whether HEAP_STR is the best choice in cmalloc_abort).

Oops, I see I used (void), out of habit, on the static function, even though this is C++.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You're allocating too much memory:

unsigned int total = nchars + 1 + sizeof cmdname + 1;

You only need 1 terminating null at the end of the string. This is already accounted for in the size of cmdname (since you declare it as a string initialized []). You can simply allocate:

unsigned int total = nchars + 1 + sizeof cmdname;

Or, if you add the "\" onto cmdname you could do:

char cmdname[] = "\\cmd.exe";
unsigned int total = nchars + sizeof cmdname;
\$\endgroup\$
0
\$\begingroup\$

This belongs in a comment (which I cannot make) but why are you declaring cmd_exe_path as const? And why is cmdname hard-declared as an array? It may improve readability if it was a char*.

\$\endgroup\$
1
  • \$\begingroup\$ cmdname was declared as an array so the size could be obtained as a compile-time constant expression with sizeof cmdname, including the null terminator. (By the time I wrote the total calculation, I forgot about the null terminator already being included, as pointed out in forsvarir's answer). cmd_exe_path is const char * because it is returned by get_cmd_exe_path, which returns const char *. That would require a cast if cmd_exe_path were char *. \$\endgroup\$ Commented Jul 8, 2016 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.