我在尝试 运行 execve 时得到了错误的地址

I get bad address when trying to run execve

所以我正在尝试一个字符串向量,其中包含我想尝试的参数和 运行 使用 execve 命令。我也在复制环境,因为我正在编写的应用程序需要从进程中获取传入环境的副本。此应用程序是用 C++ 编写的,我从 execve 调用中收到 "Bad Address" 错误。这是我当前的代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>

using namespace std; 

int main (int argc, char * argv[], char * envp[]) {

    int total = 0;
    int a = 0;
    int b = 0;
    char **my_array;
    char **my_envp;

    // Setup copy the environment.
    while (envp[a] != NULL) {
        total++;
        a++;
    }

    my_envp = new char*[total+1];

    for (a = 0; a < total; a++) {
        my_envp[a] = new char[strlen(envp[a])+1];
        strcpy(my_envp[a], envp[a]);
    }
    a++;
    my_envp[a] = NULL;

    // Get my path and arguments.
    vector<string> random = { "/bin/echo", "Grace ", "Will ", "Dan ", "Scott ", "Kevin ", "Amanda " };
    my_array = new char*[random.size()+1];

    for (b = 0; b < random.size(); b++) {
        my_array[b] = new char[strlen(random[b].c_str())+1];
        strcpy(my_array[b], random[b].c_str());
    }
    b++;
    my_array[b] = NULL;

    // Run my arguments.
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        if (execve(my_array[0], my_array, my_envp) == -1)
            perror("");
        exit(1);
    } else {
        waitpid(pid, 0, WUNTRACED);
    }

    // Clean up time.
    for (b = 0; b < random.size(); b++) 
        delete [] my_array[b];

    delete [] my_array;

    for (a = 0; a < total; a++) 
        delete [] my_envp[a];

    delete [] my_envp;

    return 0;
}

这是我的 Valgrind 输出:

{"
==27594== Memcheck, a memory error detector
==27594== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==27594== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==27594== Command: ./a.out
==27594== 
==27594== Invalid write of size 8
==27594==    at 0x40115A: main (in /home/examples/a.out)
==27594==  Address 0x5ab6eb0 is 0 bytes after a block of size 560 alloc'd
==27594==    at 0x4C2E80F: operator new[](unsigned long) (o)
==27594==    by 0x40106E: main (in /home/examples/a.out)
==27594== 
==27594== Invalid write of size 8
==27594==    at 0x4014E0: main (in /home/examples/a.out)
==27594==  Address 0x5ab9220 is 0 bytes after a block of size 64 alloc'd
==27594==    at 0x4C2E80F: operator new[](unsigned long) ()
==27594==    by 0x4013D1: main (in /home/examples/a.out)
==27594== 
==27595== Syscall param execve(argv) points to uninitialised byte(s)
==27595==    at 0x549E777: execve (syscall-template.S:84)
==27595==    by 0x40151D: main (in /home/examples/a.out)
==27595==  Address 0x5ab9218 is 56 bytes inside a block of size 64 alloc'd
==27595==    at 0x4C2E80F: operator new[](unsigned long) ()
==27595==    by 0x4013D1: main (in /home/examples/a.out)
==27595== 
==27595== Syscall param execve(envp) points to uninitialised byte(s)
==27595==    at 0x549E777: execve (syscall-template.S:84)
==27595==    by 0x40151D: main (in /home/examples/a.out)
==27595==  Address 0x5ab6ea8 is 552 bytes inside a block of size 560 alloc'd
==27595==    at 0x4C2E80F: operator new[](unsigned long) ()
==27595==    by 0x40106E: main (in /home/examples/a.out)
==27595== 
Grace  Will  Dan  Scott  Kevin  Amanda 
==27594== 
==27594== HEAP SUMMARY:
==27594==     in use at exit: 72,704 bytes in 1 blocks
==27594==   total heap usage: 80 allocs, 79 frees, 77,249 bytes allocated
==27594== 
==27594== LEAK SUMMARY:
==27594==    definitely lost: 0 bytes in 0 blocks
==27594==    indirectly lost: 0 bytes in 0 blocks
==27594==      possibly lost: 0 bytes in 0 blocks
==27594==    still reachable: 72,704 bytes in 1 blocks
==27594==         suppressed: 0 bytes in 0 blocks
==27594== Reachable blocks (those to which a pointer was found) are not show
==27594== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==27594== 
==27594== For counts of detected and suppressed errors, rerun with: -v
==27594== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
"}

我感觉我创建 cstring 指针的 char 指针的方式不正确,或者我遗漏了一些非常明显的东西。谢谢

你的代码的问题是你的环境列表和 参数列表已 NULL 终止。然后你更新了你的代码 我的建议:

What has valgrind to do with that? You do my_array = new char*[random.size() + 1]; and after the loop making the copies, you do my_array[b] = NULL

但你不正确:

for (a = 0; a < total; a++) {
    my_envp[a] = new char[strlen(envp[a])+1];
    strcpy(my_envp[a], envp[a]);
}
a++; // <-- does not belong here
my_envp[a] = NULL;

for (b = 0; b < random.size(); b++) {
        my_array[b] = new char[strlen(random[b].c_str())+1];
        strcpy(my_array[b], random[b].c_str());
}
b++; // <-- does not belong here
my_array[b] = NULL;

而 valgrind 对此表示抱怨:

==27594== Invalid write of size 8
==27594==    at 0x40115A: main (in /home/examples/a.out)
==27594==  Address 0x5ab6eb0 is 0 bytes after a block of size 560 alloc'd
==27594==    at 0x4C2E80F: operator new[](unsigned long) (o)
==27594==    by 0x40106E: main (in /home/examples/a.out)
==27594== 
==27594== Invalid write of size 8
==27594==    at 0x4014E0: main (in /home/examples/a.out)
==27594==  Address 0x5ab9220 is 0 bytes after a block of size 64 alloc'd
==27594==    at 0x4C2E80F: operator new[](unsigned long) ()
==27594==    by 0x4013D1: main (in /home/examples/a.out)

正确的版本应该是(正如我在评论中所写)

for (b = 0; b < random.size(); b++) {
        my_array[b] = new char[strlen(random[b].c_str())+1];
        strcpy(my_array[b], random[b].c_str());
}
my_array[b] = NULL;

您不需要 b++ 的原因是循环已经在执行它了。

循环

for(int i = 0; i < 5; ++i)
{
    printf("i in loop: %d\n", i);
}

printf("i out of loop\n");

你会得到

i in loop: 0
i in loop: 1
i in loop: 2
i in loop: 3
i in loop: 4
i out of loop: 5

因为当条件计算为 false 时循环结束,并且发生了这种情况 当 i == 5。这同样适用于上面的 for 循环,如果你递增 b 循环结束后,你又增加了很多。

所以假设 random.size() 是 5(就像在我的循环示例中一样)并且您已经分配了 space for random.size() + 1 == 6 个元素,所以你只能从内存中索引 从 0 到 5。在循环结束时 b 是 5,如果你做一个额外的 b++ 那么 b 是 6 并且 6 超出了 my_array.

的界限

为了证明,这是我编译的代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>

using namespace std; 

int main (int argc, char * argv[], char * envp[]) {

    // these variables must be unsigned, vector.size()
    // returns an unsigned value
    unsigned int total = 0;
    unsigned int a = 0;
    unsigned int b = 0;
    char **my_array;
    char **my_envp;

    // Setup copy the environment.
    while (envp[a] != NULL) {
        total++;
        a++;
    }

    my_envp = new char*[total+1];

    for (a = 0; a < total; a++) {
        my_envp[a] = new char[strlen(envp[a])+1];
        strcpy(my_envp[a], envp[a]);
    }
    my_envp[a] = NULL;

    // Get my path and arguments.
    vector<string> random = { "/bin/echo", "Grace ", "Will ", "Dan ", "Scott ", "Kevin ", "Amanda " };
    my_array = new char*[random.size()+1];

    for (b = 0; b < random.size(); b++) {
        my_array[b] = new char[strlen(random[b].c_str())+1];
        strcpy(my_array[b], random[b].c_str());
    }
    my_array[b] = NULL;

    // Run my arguments.
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        if (execve(my_array[0], my_array, my_envp) == -1)
            perror("");
        exit(1);
    } else {
        waitpid(pid, 0, WUNTRACED);
    }

    // Clean up time.
    for (b = 0; b < random.size(); b++) 
        delete [] my_array[b];

    delete [] my_array;

    for (a = 0; a < total; a++) 
        delete [] my_envp[a];

    delete [] my_envp;

    return 0;
}

和输出

$ g++ a.cpp -oa -g -Wall 
$ valgrind ./a 
==15833== Memcheck, a memory error detector
==15833== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15833== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15833== Command: ./a
==15833== 
Grace  Will  Dan  Scott  Kevin  Amanda 
==15833== 
==15833== HEAP SUMMARY:
==15833==     in use at exit: 0 bytes in 0 blocks
==15833==   total heap usage: 79 allocs, 79 frees, 78,730 bytes allocated
==15833== 
==15833== All heap blocks were freed -- no leaks are possible
==15833== 
==15833== For counts of detected and suppressed errors, rerun with: -v
==15833== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)