Why does GetCommandLine give me a corrupted command line?


A customer had the following problem:

We're calling GetCommandLine to retrieve the command line, and the documentation says that it returns a single null-terminated string. However, when we call it in our application, we find that it is actually a double-null-terminated string. The buffer returned consists of a series of null-terminated strings, one string per word on the command line, all stored one after the other, and with two null terminators at the end. How do I get the original string?

Recall that the command line is just a conveniently-initialized variable in a process and once it's set up, the kernel doesn't really care about it any more.

What is most likely happening is that somebody is taking the raw command line returned by GetCommandLine and writing to it. The customer can confirm this by dumping the command line just as the process starts, even before any DLLs get to run their DllMains, and then setting a write breakpoint on the command line to see who is writing to it.

And in fact, the customer did find the culprit.

It turns out it was some other part of the code (not written by me!) which was parsing the command line and writing into it in the process.

Comments (10)
  1. Jack Mathews says:

    I bet some unsuspecting person used the bane of the c standard library – strtok.  Probably one of the least safe standard functions.  Modifies de-facto input data AND keeps global state.

  2. Anonymous says:

    @Jack

    I don’t know if it’s strtok()’s fault.  If you don’t want there to be any surprises where buffers get modified out from under you, you should use the "const" keyword responsibly.  Callers of strtok() and similar functions should duplicate the buffer if they’re passed something that’s const.

  3. Ooh says:

    Wondering how often PSS + Raymond have to answer this question… Seems to come up a lot!

  4. cthulhu says:

    i bet it was written by them

  5. Médinoc says:

    It’s likely that it was modified by a strtok-style parsing, but note that this function, while not reentrant (strtok_r() is, as well as its port foolishly named strtok_s()) *is* const-correct, as it explicitly takes a non-const parameter.

    So I’d rather blame the non-const-correct C functions like strchr() and strtol()…

  6. Anonymous says:

    @Médinoc

    What I meant is that all buffers that you don’t want modified should be marked const, and you should mark things const liberally and defensively.  GetCommandLine() (and typically argv and similar) aren’t marked with const, but the broader point stands, and I don’t think this is a weakness of strtok()’s interface.

    (Also, strtok() may not be reentrant, but at least if the CRT declares the state pointer as __declspec(thread) it can be used by multiple threads, though still not in a recursive function…)

  7. Jack Mathews says:

    Oh, you people…  I didn’t say anything was strtok’s fault.  It’s the fault of the person that used strtok, which happens to be a poorly designed ancient function.

  8. Joe says:

    Windows must keep the original command line around somewhere though, because it can be retrieved via WMI.

  9. Gabe says:

    Joe: Are you saying that WMI manages to give you a non-corrupted command line? If so, it does it by making a copy when the process starts, because Windows doesn’t keep a copy anywhere.

    Honestly, though, it should. It would be nice if somebody (kernel or CSRSS) kept a copy of the original command line (possibly retrieved with GetOriginalCommandLine) so that DLLs can write proper debug logs.

  10. Rick says:

    Hehe. I remember when I tried to parse the commandline using sprintf and put the arguments backwards. Simple mistake that took down the Linux server it was running on.

Comments are closed.

Skip to main content