[英]Unknown C String Truncation/Overwrite
我有一個簡單的字符串操作有趣的內存問題。 問題本身實際上並不是在讀取字符串時,而是在我嘗試調用字符串之前。
char *removeInvalid(char *token){
fprintf(stderr," Before: %s \n", token);
char *newToken = malloc(sizeof(100) + 1);
fprintf(stderr," After: %s \n", token);
}
每當我運行它時,字符串如果在char * newToken之后被截斷,則是malloc'd。 因此打印輸出結果
Before: Willy Wanka's Chochlate Factory
After: Will Wanka's Chochlate F!
任何人都知道這是什么? 我查看了malloc的其他示例,但無法弄清楚它是如何出錯的。
編輯:以下完整代碼。 請注意我是一名剛開始學習C的大學生,所以任何人都不是完美的。 但它可以解決這個錯誤。
函數調用如下。 Main-> initialReadAVL(這部分工作正常)然后在調用commandReadAVL之后執行commandReadAVL-> ReadHelper(這里再次正常工作。然后CleanUpString-> removeSpaces(正常)然后CleanUpString-> removeInvalid(這是錯誤的地方)
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "node.h"
#include "avl.h"
#include "scanner.h"
#include "bst.h"
/* Options */
int avlSwitch = 0;
int bstSwitch = 0;
int insertSwitch = 0;
int deleteSwitch = 0;
int frequencySwitch = 0;
int displaySwitch = 0;
int statisticSwitch = 0;
int ProcessOptions(int argc, char **argv);
char *cleanUpString(char *token);
char *turnToLowerCase(char *token);
char *removeSpaces(char *token);
char *removeInvalid(char *token);
char *readHelper(FILE *in);
void Fatal(char *fmt, ...);
void preOrder(struct node *root);
void initialReadAVL(avl *mainAVL, FILE *in);
void initialReadBST(bst *mainBST, FILE *in);
void commandReadBST(bst *mainBST, FILE *commandList);
void commandReadAVL(avl *mainAVL, FILE *commandList);
int main(int argc, char **argv) {
struct avl *mainAVL;
struct bst *mainBST;
FILE *text;
FILE *commandList;
if(argc != 4){
Fatal("There must be 4 arguments of form 'trees -b corpus commands' \n");
}
int argIndex = ProcessOptions(argc,argv);
text = fopen(argv[2], "r");
commandList = fopen(argv[3], "r");
//Protect against an empty file.
if (text == NULL){
fprintf(stderr,"file %s could not be opened for reading\n", argv[2]);
exit(1);
}
if (commandList == NULL){
fprintf(stderr,"file %s could not be opened for reading\n", argv[3]);
exit(1);
}
if (avlSwitch){
mainAVL = newAVL();
initialReadAVL(mainAVL, text);
preOrder(mainAVL->root);
fprintf(stderr,"\n");
commandReadAVL(mainAVL, commandList);
preOrder(mainAVL->root);
fprintf(stderr,"\n");
}
else if (bstSwitch){
mainBST = newBST();
initialReadBST(mainBST, text);
preOrder(mainBST->root);
commandReadBST(mainBST, commandList);
preOrder(mainBST->root);
}
return 0;
}
void commandReadAVL(avl *mainAVL, FILE *commandList){
char *command;
char *textSnip;
while(!feof(commandList)){
command = readHelper(commandList);
textSnip = readHelper(commandList);
textSnip = cleanUpString(textSnip);
if(command != NULL){
switch (command[0]) {
case 'i':
fprintf(stderr,"%s \n", textSnip);
insertAVL(mainAVL, textSnip);
break;
case 'd':
deleteAVL(mainAVL, textSnip);
break;
case 'f':
break;
case 's':
break;
case 'r':
break;
default:
Fatal("option %s not understood\n",command);
}
}
}
}
void commandReadBST(bst *mainBST, FILE *commandList){
char *command;
char *textSnip;
while(!feof(commandList)){
command = readHelper(commandList);
textSnip = readHelper(commandList);
textSnip = cleanUpString(textSnip);
if(command != NULL){
switch (command[0]) {
case 'i':
insertBST(mainBST, textSnip);
break;
case 'd':
deleteBST(mainBST, textSnip);
break;
case 'f':
break;
case 's':
break;
case 'r':
break;
default:
Fatal("option %s not understood\n",command);
}
}
}
}
char *readHelper(FILE *in){
char *token;
if (stringPending(in)){
token = readString(in);
}
else {
token = readToken(in);
}
return token;
}
void initialReadBST(bst *mainBST, FILE *in){
char *token;
while(!feof(in)){
token = readHelper(in);
token = cleanUpString(token);
if (token != NULL){
insertBST(mainBST, token);
}
}
}
void initialReadAVL(avl *mainAVL, FILE *in){
char *token;
while(!feof(in)){
token = readHelper(in);
token = cleanUpString(token);
if (token != NULL){
insertAVL(mainAVL, token);
}
}
}
//Helper Function to clean up a string using all the prerequisites.
char *cleanUpString(char *token){
char *output = malloc(sizeof(*token)+ 1);
if (token != NULL){
output = removeSpaces(token);
fprintf(stderr,"before : %s \n", output);
output = removeInvalid(output);
fprintf(stderr,"%s \n", output);
output = turnToLowerCase(output);
return output;
}
return NULL;
}
//Helper function to turn the given string into lower case letters
char *turnToLowerCase(char *token){
char *output = malloc(sizeof(*token) + 1);
for (int x = 0; x < strlen(token); x++){
output[x] = tolower(token[x]);
}
return output;
}
//Helper function to remove redundent spaces in a string.
char *removeSpaces(char *token){
char *output;
int x = 0;
int y = 0;
while (x < strlen(token)){
if (token[x]== ' ' && x < strlen(token)){
while(token[x] == ' '){
x++;
}
output[y] = ' ';
y++;
output[y] = token[x];
y++;
x++;
}
else {
output[y] = token[x];
y++;
x++;
}
}
return output;
}
char *removeInvalid(char *token){
fprintf(stderr," Before: %s \n", token);
char *newToken = malloc(sizeof(* token)+ 1);
fprintf(stderr," After: %s \n", token);
int x = 0;
int y = 0;
while (x < strlen(token)){
if (!isalpha(token[x]) && token[x] != ' '){
x++;
}
else {
newToken[y] = token[x];
y++;
x++;
}
}
return newToken;
}
//Processes a system ending error.
void Fatal(char *fmt, ...) {
va_list ap;
fprintf(stderr,"An error occured: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(-1);
}
//Processes the options needed to be executed from the command line
int ProcessOptions(int argc, char **argv) {
int argIndex;
int argUsed;
int separateArg;
argIndex = 1;
while (argIndex < argc && *argv[argIndex] == '-')
{
/* check if stdin, represented by "-" is an argument */
/* if so, the end of options has been reached */
if (argv[argIndex][1] == '\0') return argIndex;
separateArg = 0;
argUsed = 0;
if (argv[argIndex][2] == '\0')
{
separateArg = 1;
}
switch (argv[argIndex][1])
{
case 'b':
bstSwitch = 1;
break;
case 'a':
avlSwitch = 1;
break;
default:
Fatal("option %s not understood\n",argv[argIndex]);
}
if (separateArg && argUsed)
++argIndex;
++argIndex;
}
return argIndex;
}
void preOrder(struct node *root) {
if(root != NULL)
{
fprintf(stderr,"%s ", root->key);
preOrder(root->lChild);
preOrder(root->rChild);
}
}
ReadString()
char *
readString(FILE *fp)
{
int ch,index;
char *buffer;
int size = 512;
/* advance to the double quote */
skipWhiteSpace(fp);
if (feof(fp)) return 0;
ch = fgetc(fp);
if (ch == EOF) return 0;
/* allocate the buffer */
buffer = allocateMsg(size,"readString");
if (ch != '\"')
{
fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
fprintf(stderr,"first character was <%c>\n",ch);
exit(4);
}
/* toss the double quote, skip to the next character */
ch = fgetc(fp);
/* initialize the buffer index */
index = 0;
/* collect characters until the closing double quote */
while (ch != '\"')
{
if (ch == EOF)
{
fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
fprintf(stderr,"no closing double quote\n");
exit(6);
}
if (index > size - 2)
{
++size;
buffer = reallocateMsg(buffer,size,"readString");
}
if (ch == '\\')
{
ch = fgetc(fp);
if (ch == EOF)
{
fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
fprintf(stderr,"escaped character missing\n");
exit(6);
}
buffer[index] = convertEscapedChar(ch);
}
else
buffer[index] = ch;
++index;
ch = fgetc(fp);
}
buffer[index] = '\0';
return buffer;
}
INPUT:Commands.txt
i "Willy Wonka's Chochlate Factory"
INPUT testFile.txt
a b c d e f g h i j k l m n o p q r s t u v w x y z
謝謝!
幾乎可以肯定,在您未向我們展示的代碼的某些部分中存在緩沖區溢出。 如果我猜測,我會說你為token
分配的存儲空間太小,以至於包含你首先寫入的完整字符串。
您是否有機會使用removeInvalid()
的相同錯誤代碼分配token
:
malloc(sizeof(100) + 1);
^^^^^^^^^^^ this doesn't allocate 101 characters, it allocates sizeof(int)+1
char *readHelper(FILE *in){
char * token = malloc(sizeof(char *) + 1);
if (stringPending(in)){
token = readString(in);
}
else {
token = readToken(in);
}
return token;
}
如果沒有能夠看到readString
或readToken
,很難理解這readToken
,但這可能不對。
首先,為指向一個或多個字符的指針分配一個字節。 這樣的事情會有什么用處? 如果您沒有存儲指向一個或多個字符的指針,為什么要使用sizeof(char *)
? 如果要存儲指向一個或多個字符的指針,為什么要添加一個? 很難想象導致這一行代碼的推理。
然后,在if
,你會立即失去從malloc
返回的值,因為你通過使用它來存儲其他內容來覆蓋token
。 如果您不打算使用分配給token
的值,為什么要分配它?
坦率地說,很多代碼根本沒有任何意義。 沒有評論,很難理解推理,所以我們可以指出它有什么問題。
要么在這行代碼背后有推理,在這種情況下它只是完全錯誤的推理。 或者更糟糕的是,代碼行沒有任何推理,希望它能以某種方式工作。 這兩種方法都不會產生工作代碼。
當您嘗試調試代碼時,首先刪除您通過實驗添加或不理解的任何內容。 如果你理解malloc(sizeof(char *) + 1)
,那么請解釋你的想法,以便你的理解得到糾正。
為什么你認為你需要一個比指向一個或多個字符的指針大一個字節的緩沖區?
char *turnToLowerCase(char *token){
char *output = malloc(sizeof(*token) + 1);
for (int x = 0; x < strlen(token); x++){
output[x] = tolower(token[x]);
}
return output;
}
這可能是你的主要問題。 您為兩個字符分配了足夠的空間,然后繼續存儲多個字符。 你可能想要:
char *output = malloc(strlen(token) + 1);
由於token
是char*
, *token
是char
。 所以sizeof(*token)
是sizeof(char)
- 絕對不是你想要的。
在David Schwartz和其他海報的幫助下,我能夠找到問題中的錯誤。 當我為我的令牌/輸出分配內存時,我沒有分配足夠的空間..使用錯誤的代碼
malloc(sizeof(100) + 1);
和
malloc(sizeof(*token) + 1);
兩者都只產生了幾個字節要分配。 這導致緩沖區問題導致隨機字母和數字/截斷發生。 第一個導致空間等效於int + 1,第二個導致char + 1.(因為我正在使用sizeof 標記,它只是它最初開始的大小,一個字符 )
為了解決這個問題,我將令牌變量的分配更改為
malloc(strlen(token) + 1);
這會分配一個等於token + 1的“字符串”長度的空間。為我的問題留出適當的空間,最終會占用<= token的空間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.