strcat()覆盖我的字符串的问题

时间:2011-02-28 17:42:07

标签: c operating-system posix

我正在编写自己的shell,并且看起来有一些strcat()意外覆盖字符串的问题。

问题是尝试在本地目录中执行文件时。它应该搜索的路径中的第二个值是'。'并且第一个是/ bin,但是当将命令附加到/ bin以获得给execlp()的绝对路径时,该命令也会被该命令覆盖。我知道MYPATH的事情很奇怪,用#s分隔是很奇怪的,但这与问题无关。

如果您想运行它以查看我正在谈论的内容,我会添加一些有用的printf()语句。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <sys/stat.h>

void execute(char**, int, char**, int);

int main (){
  char *command, *mypath, *buffer, *arglist[1024], *pathlist[1024], **ap;
  buffer = malloc(1024);
  int loop = 1;

  while (loop == 1){
    int argnum = 0, pathnum = 0;
    mypath = malloc(1024);
    if(getenv("MYPATH") == NULL)
      strcpy(mypath, "/bin#.");
    else
      strcpy(mypath, getenv("MYPATH"));
    printf("myshell$ ");
    command = readline("");

    if(strcmp(command, "exit") == 0 || strcmp(command, "quit") == 0)
      return 0;

    if(strcmp(command, "") == 0)
      continue;

    /*Tokenizes Command*/
    for(ap = arglist; (*ap = strsep(&command, " \t")) != NULL;){
      argnum++;
      if(**ap != '\0')
    if(++ap >= &arglist[1024])
      break;
    }

    /*Tokenizes Path*/
      for(ap = pathlist; (*ap = strsep(&mypath, "#")) != NULL;){
    pathnum++;
      if(**ap != '\0')
    if(++ap >= &pathlist[1024])
      break;
      }
      execute(pathlist, pathnum, arglist, argnum);
  }
  return 0;
}

void execute(char *pathlist[], int pathnum, char *arglist[], int argnum){
  pid_t pid;
  int i;
  int found = 0;
  struct stat buf;
  for(i = 0; i < pathnum; i++){
    if (found == 1)
      break;
    printf("pathlist[0]: %s\n", pathlist[0]);
    printf("pathlist[1]: %s\n", pathlist[1]);
    strcat(pathlist[i], "/");
    strcat(pathlist[i], arglist[0]);
    printf("Pathlist[0] after strcat: %s\n", pathlist[0]);
    printf("Pathlist[1] after strcat: %s\n", pathlist[1]);
    if(stat(pathlist[i], &buf) == 0){
      found = 1;
      pid = fork();
      if(pid == -1)
    printf("Error: Fork Failed\n");
      else if(pid == 0){
    if(argnum == 0)
      execlp(pathlist[i], arglist[0], (char *) NULL);
    else if(argnum == 1)
      execlp(pathlist[i], arglist[0], arglist[1], (char *) NULL);
    else if(argnum == 2)
      execlp(pathlist[i], arglist[0], arglist[1], arglist[2], (char *) NULL);
    else if(argnum == 3)
      execlp(pathlist[i], arglist[0], arglist[1], arglist[2], (char *) NULL);
      }
      else if(strcmp(arglist[argnum-1], "&") != 0){
    wait(NULL);
      }
    }
    else if(stat(pathlist[i], &buf) == -1 && i == pathnum-1 && found == 0)
      printf("Error: Command '%s' not found.\n", arglist[0]);
  }
}

2 个答案:

答案 0 :(得分:2)

strsep()不会创建它返回的令牌的独立副本。它修改原始字符串,用'#'替换分隔符(在本例中为'\0'字符),并返回指向原始字符串中标记开头的指针。

这意味着您的pathlist[]指针都指向mypath指向的字符串 - 在您调用execute()时,它看起来像这样:

               '/'   'b'   'i'   'n'   '\0'  '.'   '\0'
                ^                             ^
mypath ---------/                             |
                ^                             |
pathlist[0] ----/                             |
                                              |
pathlist[1] ----------------------------------/

pathlist[2] (null)

您现在可以看到为什么观察您的行为了 - 当您在strcat()上调用pathlist[0]时,它会从第一个'\0'开始覆盖该数组。 pathlist[1]仍然指向同一位置,但该位置的字符串内容已被strcat()覆盖。

execute()函数中,您不应尝试直接连接到pathlist[i]。而是在临时位置构造一个新字符串:

char execpath[4096];

if (snprintf(execpath, sizeof execpath, "%s/%s", pathlist[i], arglist[0]) >= sizeof execpath) {
    /* Path was too long, handle the error */
}

if (stat(execpath, &buf) == 0) {

答案 1 :(得分:0)

我必须承认我没有测试过您的代码,甚至没有仔细阅读过,但我建议完全删除strcat。这是不安全的,因为它可能会在没有通知的情况下写入缓冲区的末尾。

您可能想要定义一些安全字符串函数并改为使用它们:

char *xstrdup(char const *s)
{
    char *copy = strdup(s);
    if (copy == NULL) {
        fprintf(stderr, "error: cannot copy string: %s\n", strerror(errno));
        exit(1);
    }
    return copy;
}

char *xstrcat(char *s, char const *append)
{
    size_t len = strlen(s) + strlen(append) + 1;
    size_t news = realloc(s, len);
    if (news == NULL) {
        free(s);
        fprintf(stderr, "error: cannot append strings: %s\n", strerror(errno));
        exit(1);
    }
    strcat(news, append);
    return news;
}