14

I apologize in advance for the useless title of this question, but nothing seemed to fit better.

The idea here is to replicate argv in another variable, essentially making a copy of it. So the basic idea of what the function does is, use malloc() to request some space for a copy and then iterate through argv making copies of each element.

This is the code I'm working with, the development environment is right now Visual Studio 2019 (even if it's not strictly a C compiler...):

// Returns a copy of an array of strings (intended for argv, but should work with any of them):
wchar_t** copyArgv(size_t argc, wchar_t* argv[]) {
    // Allocate space for the array of arguments:
    wchar_t** argsCopy = malloc(((argc + 1) * sizeof(wchar_t*)));
    if (!argsCopy)
        return NULL;
    // Copy each one of them:
    for (size_t i = 0; i < argc; i++) {
        argsCopy[i] = _wcsdup(argv[i]);
        if (!argsCopy[i]) {
            // Should also free any previous copied string I left that part out in the paste.
            free(argsCopy);
            return NULL;
        }
    }
    argsCopy[argc] = NULL;
    return argsCopy;
}

I've been trying different ways to make a copy of argv but each and everyone of them lets VS to believe there can be a buffer overrun when I make a copy of an argument (line:argsCopy[i] = _wcsdup(argv[i]);) or reading invalid data in the next line, meaning reading out of the bounds of the reserved space.

All of this has lead me to believe the problem lies in the (now) only malloc() call to reserve space for the array of arguments.

Yet I'm banging my head against the wall trying to figure out what the problem is, I mean, I think I'm asking for enough space.

I've tried other compilers as well, latest stable versions of Clang and GCC don't seem to show any such warning. So I decided to ask you, seasoned programmers, if you can spot the problem, or it's some sort of compiler bug (unlikely I bet).

For reference these are the exact warnings VS2019 is throwing (in a 64-bit compilation):

In the assignment:

Buffer overrun while writing to 'argsCopy': the writable size is '((argc+1))*sizeof(wchar_t *)' bytes, but '16' bytes might be written.

Next line, the test for NULL:

Reading invalid data from 'argsCopy': the readable size is '((argc+1))*sizeof(wchar_t *)' bytes, but '16' bytes may be read.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 2
    (Not related to the question.) You forgot to initialize last array element: `argsCopy[argc] = NULL`. – gudok May 09 '19 at 10:11
  • 1
    I don't see a problem with either of the lines that's getting those warnings. – Barmar May 09 '19 at 10:14
  • I think this is a VS bug. – Barmar May 09 '19 at 10:15
  • I found several similar reports at developercommunity.visualstudio.com. – Barmar May 09 '19 at 10:16
  • E.g. https://developercommunity.visualstudio.com/content/problem/542393/code-analysis-incorrect-buffer-overrun-warning-c63.html – Barmar May 09 '19 at 10:17
  • Thank you everyone! I completely forgot about the terminating NULL @gudok even though I reserved 1 more for it. I'm going to comment in that problem you posted with this code Bamar, but I bet I'll need to create an account there. – Alexander Row May 09 '19 at 10:29
  • (Nevermind what I said. A terminating NULL is indeed provided. Your copy might not need it, though) – ikegami May 09 '19 at 10:35
  • 1
    The command line arguments are of unknown length. You can check if there's a `\0` within the "MAX" first characters in each string, before copying the data. Where the 16 bytes thing comes from, I have no idea. – Lundin May 09 '19 at 11:18
  • @Lundin those bytes seem to be architecture dependent because if I target a 32-bit one the warning reads 8 instead. But I was careful (I think) to use architecture independent types, like those `size_t` instead of `int` for the iterations for example just in case. I'll try to get a Linux VM up to see if Valgrind is able to show something else. – Alexander Row May 09 '19 at 11:25
  • 1
    I'm talking about the strings themselves. Unrelated to that, your format of main() is not standard. Besides, it is not for the programmer to decide the format of main(), you may only use formats specified by your compiler. – Lundin May 09 '19 at 11:28
  • Oh, that was because of the (weird?) way Unicode works in Windows. Not only the `main` signature changes, but there are some behind the scenes conversions that mess things up for console programs (I/O of Unicode stuff seems tricky). Switching to the usual `char`, `strdup`, and `main` it's still showing the same warnings over here. – Alexander Row May 09 '19 at 13:34
  • Are you missing the proper includes in your file to include the function declaratoin for _wcsdup? Depending on the version of the C compiler settings for warnings/errors, if the compiler doesn't find the declaration fo _wcsdup, it'll implicitly assume that it returns an int. – bevenson May 09 '19 at 13:45
  • Well, it could be correct if `argc == std::numeric_limits::max()`. – aschepler May 09 '19 at 21:44
  • Maybe you should also malloc each `(wchar_t*)` inside `for` loop, not only `(wchar_t**)` – Alejandro Galera Jun 04 '19 at 09:11

4 Answers4

1

These are warnings from the static analyzer. It tries, for example, to recognize buffer overflow situations.

Warning

It is important to note that these are warnings and not error messages. The compiler says that there might be something potentially wrong. Static analysis is generally a difficult thing.

False Positive

There is no buffer overrun situation, so it is a false positive. I would assume, that this message disappears in a future update.

Change the Code a Bit

If we change the memory allocation line as follows:

wchar_t** argsCopy = (wchar_t**)calloc(argc + 1, sizeof(wchar_t*));

then there will be no more warnings from Visual Studio 2019.

The number of bytes allocated remains the same. However, the warnings disappear.

Test

Before the change the VS Error list looks like this:

before

After the application of my proposed changes, the warnings have disappeared:

after

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • 2
    No cast is needed to convert a `void*` to another pointer type. (One is needed in C++, but the question is tagged C.) – aschepler May 09 '19 at 21:42
  • 1
    You're right. My personal preference is to use explicit casts in such places - also in C. In C++ it would be even required. Matter of taste I would say :-) – Stephan Schlecht May 09 '19 at 21:54
  • Many pros and cons (with varying conclusions) are discussed at https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – aschepler May 09 '19 at 21:58
  • I see your point, that this is regarded as controversy. Regarding implicit functions: should not be a problem with modern C compilers. Anyway, I will remove the additional hint from the answer - it is not directly related to the actual question or answer anyway. – Stephan Schlecht May 09 '19 at 22:08
  • Note that `calloc` zeroes the allocated memory, so the explicit setting the last pointer to NULL won't be needed. Also there's tiny bit of extra overhead of clearing memory which is going to be initialized anyway, but this should be irrelevant in basically any use case I can think of. – hyde May 15 '19 at 20:01
  • I agree with both of your statements. It's interesting that after an equivalent transformation of this one line of code (same number of bytes are allocated) the static analyzer can correctly detect that there is no buffer overrun problem. – Stephan Schlecht May 15 '19 at 20:18
  • @hyde: While every implementation I know of uses all zeroes to represent a null pointer, it's not guaranteed to be universally true in either C or C++. – Adrian McCarthy Aug 13 '19 at 20:59
0

I could be mistaken, however, after playing around with an online copy of visual studio (https://rextester.com/l/c_online_compiler_visual), I am forced to assume that you have forgotten to include string.h or wchar.h (either one works). Visual Studio appears to assume that your return type is an integer instead of a wchar_t* as the function is not defined. It seems that there is a bit of "magic" due to the fact that it is a reserved function starting with an _ and therefore it doesn't issue other warnings? Again without your exact environment though I am forced to partially speculate (your comment on your target changing the warning gave me a hopefully correct hint).

Greenbeard
  • 508
  • 5
  • 14
0

The key point might be you didn't malloc the enough space to hold the data you want to copy.

I don't know whether I actually understand what you want to do, I assume you want to copy two-dimensional character array to another memory segment and then return its address, and the array has 'argc' lines, the address of each line of string is stored in the array of argv.

But why did you use argc+1 instead of argc? For malloc extra space to prevent buffer overrun? and more important is sizeof(wchar_t*) will return the size of a pointer ( 8 bytes in 64 bits system ). It will not return the size of one of the string in the two-dimensional array we want.

Green
  • 121
  • 1
  • 3
-2

1) One way to replicate argv is explained below but., 2) I am unable to understand why you want to make a replica of argv? what use-case/user-problems does it solve?

as I mentioned in (1), here is one of the ways, which is essentially about copying all the contents that argv has into your buffer. It goes something like this (PS: there could be compilation erros as I"m typing in my phone while being in a taxi, so don't have access to a hi quality C compiler to cross check)

int numArgc = argc
char** argvCopy;

for (i=0;i<argc,i++)
{

 argvCopy[i] = malloc(sizeof(char)*strlen(argv[i]));
 strcpy(argvCopy[i], argv[i]);

}

//please do not forget to Free this malloc'ed memory (a very common C programming error) //when you don't need it anymore 

please do tell the problem that you want to solve

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
ARD
  • 48
  • 8
  • 1
    I don't believe the code you have here will work - since `argvCopy` is uninitialized, the line `argvCopy[i] = ...` results in undefined behavior. – templatetypedef May 31 '19 at 23:49