7

I am trying to turn a vector of char* into an array of char pointer but I get this annoying error and I can't figure out what I'm doing wrong.

char** Parse::toCommand(std::vector<char*> command) {
    char** array = new char* [command.size()];
    for (int i = 0; i < command.size(); i++) {
        array[i] = command[i];
    }

    return array;
}

I get this warning which causes my program to not run.

 Buffer overrun while writing to 'array':  the writable size is 'command.public: unsigned int __thiscall std::vector<char *,class std::allocator<char *> >::size(void)const ()*4' bytes, but '8' bytes might be written.

the char* is actually a c string of course.

The strings in the vector are pieces of a string that was cut up using strtok_s. I got rid of the Null at the end of every string by converting each to a c str using string::copy() to get a non constant c string and using the constructor of std::string to get a regular string. I then popped the back to rid myself of the null.

My end goal is I want to have an array of c strings so that I can pass it to execvp()

for (int i = 0; i < exes.size(); i++) {  //Separate each executable and argument list into vector of char* and push that to 2d vector of char*

        char* arg = exes[i]; //arg = the entire string executable and arguments
        std::vector <char*> argV;

        char* place = NULL;

        ptr3 = strtok_s(arg, " ", &place);

        while (ptr3 != NULL) {

            if (*ptr3 == '"') {//if beginning of remaining char* begins with ", push char*
                std::string temp;
                temp.push_back(*ptr3);
                ptr3 = strtok_s(NULL, "\"", &place);
                temp.append(ptr3);
                temp.pop_back();
                temp.push_back('"');
                char* cstr = new char[temp.size()];
                temp.copy(cstr, temp.size(), 0);
                argV.push_back(cstr);
            }
            else if (*ptr3 == '#') {
                break;
            }
            else {
                std::string temp(ptr3);
                temp.pop_back();
                char* cstr = new char[temp.size()];
                temp.copy(cstr, temp.size(), 0);
                argV.push_back(cstr);
            }
            ptr3 = strtok_s(NULL, " ", &place);
        }

        argV.push_back(NULL);
        args.push_back(argV);
    }

    for (int i = 0; i < args.size(); i++) {
        char** command = this->toCommand(args[i]);
        commands[i] = new COM(command);
    }

argV is a vector<vector<char*>> and

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
dthecsguy
  • 85
  • 7
  • 2
    Why do you have `char*` as strings instead of `std::string`? And if you just wanted a C-style "array" of strings, you could get it from your `command` vector by using [`command.data()`](https://en.cppreference.com/w/cpp/container/vector/data), or simply `&command[0]`. – Some programmer dude Nov 12 '19 at 07:17
  • 1
    Bro everything I’m doing has a reason for it. I need help with the error. I don’t quite understand it. – dthecsguy Nov 12 '19 at 07:19
  • This isn't your issue, but you can avoid some overhead by passing `command` by reference instead of by value. Avoids the vector copy. – selbie Nov 12 '19 at 07:22
  • 1
    How you generate that warning?because I cannot see it when using clang or gcc. what is your compile option? are you using any 3rd party tool for static analyzing? – Afshin Nov 12 '19 at 07:23
  • 1
    That error message doesn't look like a typical compiler error message. When and where do you get it? Is it the only output you get? There's no informational notes (or any other output at all)? And please try to create a [mcve] to show us, while the error might be reported in that piece of code it might originate somewhere else? And how certain are you that this is the piece of code that causes the reporting of the error? How do you know? – Some programmer dude Nov 12 '19 at 07:23
  • Visual Studio [C6386](https://learn.microsoft.com/en-us/visualstudio/code-quality/c6386?view=vs-2019) – acraig5075 Nov 12 '19 at 07:25
  • 1
    Im using visual studio but in the end program will be compiled on c make i will edit question – dthecsguy Nov 12 '19 at 07:26
  • Could the c_strings not being null terminated cause a problem? – dthecsguy Nov 12 '19 at 07:29
  • 1
    Can't reproduce: https://godbolt.org/z/ScYray. What is different in you setup? – Daniel Langr Nov 12 '19 at 07:30
  • The code you show doesn't have any buffer overruns. And for the code you show it doesn't matter what the pointers in the vector are pointing to. But if they're supposed to be null-terminated byte strings then they do need a null-terminator (sooner or later the lack of terminator will cause problems, but not in the function you show). – Some programmer dude Nov 12 '19 at 07:31
  • I have edited the question with more details. – dthecsguy Nov 12 '19 at 07:37
  • @dthecsguy I see the warning go away if you declare `array` with 1 more than command.size(), so presumably it is a null terminator that is expected and you should cater for it. – acraig5075 Nov 12 '19 at 07:41
  • Note that while `std::string` is guaranteed to add a null-terminator, it's not counted as part of the string. "Popping" the back of the string will simply remove the last character of the string, the terminator will still be there in the string. – Some programmer dude Nov 12 '19 at 07:41
  • And if you're supposed to pass this array to one of the `exec` functions, then they *must* be null-terminated. And the first element in the array should be the "command", and the last element in the array should be a null pointer. And as mentioned in my first comment, you don't need this function, as you can get the pointer you need from the vector itself. – Some programmer dude Nov 12 '19 at 07:42
  • Ahh so each when reading a c-string the null is ignored and is only used a a signal of where to stop reading. That makes total sense actually – dthecsguy Nov 12 '19 at 07:43
  • Please post answer so I can mark it. – dthecsguy Nov 12 '19 at 07:44
  • I will post other parts of code in 15min – dthecsguy Nov 12 '19 at 07:50
  • @dthecsguy no need to post more code, see my answer below. – Jabberwocky Nov 12 '19 at 07:51
  • added my code that gets this error – dthecsguy Nov 12 '19 at 08:10
  • 1
    Relevant answer: https://stackoverflow.com/a/41944194/580083. – Daniel Langr Nov 12 '19 at 08:14

2 Answers2

3

To be clear: this warning is not from the compiler but it's from the Microsoft code analyzer.

I made a complete example that reproduces the warning using Visual Studio 2019. There are no more strings, pointers to pointers and other poor design and the warning is still there.

For me the code is correct and it's simply a bug in the Microsoft code analyzer, or the analyzer is over cautious.

#include <iostream>
#include <vector>

int* toCommand(std::vector<int> command)
{
  int* array = new int [command.size()];
  for (size_t i = 0; i < command.size(); i++) {
    array[i] = command[i];
  }

  return array;
}

int main()
{
  std::vector v{1,2,3};
  int* foo = toCommand(v);
  for (int i = 0; i < 3; i++)
    std::cout << foo[i] << "\n";
}

The warning comes from the Microsoft Code analyzer and it's also displayed directly in the IDE:

enter image description here

This is the simplest example that shows the warning:

int* Foo(int size)
{
  int* array = new int [size];
  array[1] = 1;  // with array[0] = 1; the warning goes away
  return array;
}

warning C6386: Buffer overrun while writing to 'array': the writable size is 'size*4' bytes, but '8' bytes might be written.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
0

The problem is not in the code you are showing but somewhere else. Your code allocates rooms for pointers and then assigns those pointers.

The big question is however: did you really evaluate pros and cons of using char * to represent strings instead of std::string? Looks aiming for troubles...

Also your code already depends on std::vector, why using manually allocated char ** instead of a std::vector<std::string>? With a char** the way you use it there is for example no way you can know how many pointers are there (there is no way to implement size() in a portable way).

EDIT

From comments it seems you need to call execvp. From the man page:

The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed.

The array of pointers must be terminated by a NULL pointer.

Note the last phrase. You need to have one extra pointer set to NULL so that the command can know where the list of strings terminates.

Community
  • 1
  • 1
6502
  • 112,025
  • 15
  • 165
  • 265
  • 1
    I cant use string because I will be using execvp which takes char * and char*[] – dthecsguy Nov 12 '19 at 07:20
  • 2
    Then you probably need to allocate one extra pointer and set it to `nullptr` – 6502 Nov 12 '19 at 07:22
  • 1
    @dthecsguy std::string has c_str(), so your argument is invalid. – nada Nov 12 '19 at 07:38
  • C_str() returns const char* which is of no use to me – dthecsguy Nov 12 '19 at 07:40
  • @dthecsguy According to [this man page](https://linux.die.net/man/3/execvp), `execvp` takes `const char*` and `char* const []`. – Daniel Langr Nov 12 '19 at 07:55
  • As long as the `std::string` is still alive when you do your `execvp()` call, there's no harm in casting away that `const`. `std::string` needs to allocate its memory, it needs to be able to write to it, so it's not fundamentally `const`. `std::string` just doesn't like you to modify its memory under its nose, which `execvp()` doesn't do anyways. The bigger danger of using `execvp()` from C++ is, that it will tear down your process immediately, *without bothering to call any destructors*. – cmaster - reinstate monica Nov 12 '19 at 08:00
  • From C++17 you have `char* std::string::data()` (as well as `const char* std::string::data() const`) – Caleth Nov 12 '19 at 10:09