涉及指针的意外 C 行为
Unexpected C behavior involving pointers
我试图让一个 char*
变量指向一个 malloc 的字符串,但确实发生了一些错误,我正在处理一个分段错误。
那是我的代码涉及的部分,我试图给出一个可执行示例,但使用 AF_UNIX 套接字恐怕它不容易测试。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdarg.h>
#define MSG_LEN 64
int vasprintf(char** str, const const char* format, va_list args) {
size_t size;
va_list tmp;
va_copy(tmp, args);
size = vsnprintf(NULL, 0, format, tmp);
va_end(tmp);
if (size == -1) {
return -1;
}
if ((*str = (char*)malloc(sizeof(char) * (size + 1))) == NULL) {
return -1;
}
size = vsprintf(*str, format, args);
return size;
}
int asprintf(char** str, const char* format, ...) {
size_t size;
va_list args;
if (str == NULL) {
return 1;
}
va_start(args, format);
size = vasprintf(str, format, args);
va_end(args);
return size;
}
typedef struct {
int socket;
struct sockaddr addr;
socklen_t addrlen;
} connection_t;
int connection_init(connection_t* connection, const char* address, const uint16_t port) {
if (!port) {
if ((connection->socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
return -1;
}
memset(&connection->addr, 'x', sizeof(connection->addr));
((struct sockaddr_un*) & connection->addr)->sun_family = AF_UNIX;
((struct sockaddr_un*) & connection->addr)->sun_path[0] = '[=10=]';
strncpy(((struct sockaddr_un*) & connection->addr)->sun_path + 1, address, strlen(address));
connection->addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(address);
return 0;
}
}
int connetcion_connect(const connection_t* connection) {
if (connect(connection->socket, &connection->addr, connection->addrlen) == -1) {
return -1;
}
return 0;
}
int connection_send(const connection_t* connection, const char* buff) {
ssize_t len;
if ((len = send(connection->socket, buff, strlen(buff), 0)) == -1) {
return -1;
}
return len;
}
int connection_recv(const connection_t* connection, char** buff) {
ssize_t len;
if (((*buff) = (char*)malloc(sizeof(char) * (MSG_LEN + 1))) == NULL) {
return -1;
}
memset((*buff), 0, sizeof(char) * (MSG_LEN + 1));
len = recv(connection->socket, (*buff), MSG_LEN, 0);
if (len == -1 || len > MSG_LEN) {
free((*buff));
(*buff) = NULL;
return -1;
}
(*buff)[len] = 0;
/* Resize if string is shorter than len */
if (realloc((*buff), sizeof(char) * (strlen((*buff)) + 1)) == NULL) {
free((*buff));
(*buff) = NULL;
return -1;
}
return len;
}
int foo(char* query, char** result) {
connection_t con;
char* filename;
if (asprintf(&filename, "%s%s", getenv("HOME"), "/.directory/socket") == -1) {
return 1;
}
if (connection_init(&con, filename, 0) == -1) {
return 1;
}
free(filename);
if (connetcion_connect(&con) == -1) {
return 1;
}
if (connection_send(&con, query) == -1) {
return 1;
}
printf("Address %p in foo point to:\n", result);
printf("%p\n", *result);
if (connection_recv(&con, result) == -1) {
return 1;
}
printf("Address %p in foo point to:\n", result);
printf("%p\n", *result);
return 0;
}
int main(int argc, char *argv[]){
if (argc == 3 && !strncasecmp(argv[1], "hello", 5)) {
char* buff = NULL;
printf("Address %p in main point to:\n", &buff);
printf("%p\n", buff);
if(foo(argv[2], &buff)){
return 1;
}
printf("Address %p in main point to:\n", &buff);
printf("%p\n", buff);
printf("%s\n", buff);
}
return 0;
}
预期输出:
Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7ffd4c0c0d28" in main point to:
0x1bfa6a0
实际输出:
Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7f74656b6367" in main point to:
Segmentation fault
我能想到的唯一可能的解释是我以某种方式损坏了堆栈,如果需要其他资源请告诉我,我会尽快提供它们
附加信息:
尝试在 return 0
之前 printf("%s\n", *result)
我得到预期的字符串。
服务器使用相同的connection_*
功能与客户端通信。
我在 RHEL8.1 上,我正在用 gcc 8.3.1 编译它
您的 connection_t
只有一个普通的 struct sockaddr addr
成员,它只是基本结构。所需的实际数据大小因协议而异。当我 运行 你的代码时,con
分配在 ...29a0 并且有 24 个字节,但是 strncpy
试图将 41 个字节写入 ...29a7,因此超过 运行结构体。您需要为协议所需的特定结构分配内存。可能你的 addr
成员应该是一个指针,你应该为它分配内存,将指针设置为指向该内存,并调整你的其他代码。
我试图让一个 char*
变量指向一个 malloc 的字符串,但确实发生了一些错误,我正在处理一个分段错误。
那是我的代码涉及的部分,我试图给出一个可执行示例,但使用 AF_UNIX 套接字恐怕它不容易测试。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdarg.h>
#define MSG_LEN 64
int vasprintf(char** str, const const char* format, va_list args) {
size_t size;
va_list tmp;
va_copy(tmp, args);
size = vsnprintf(NULL, 0, format, tmp);
va_end(tmp);
if (size == -1) {
return -1;
}
if ((*str = (char*)malloc(sizeof(char) * (size + 1))) == NULL) {
return -1;
}
size = vsprintf(*str, format, args);
return size;
}
int asprintf(char** str, const char* format, ...) {
size_t size;
va_list args;
if (str == NULL) {
return 1;
}
va_start(args, format);
size = vasprintf(str, format, args);
va_end(args);
return size;
}
typedef struct {
int socket;
struct sockaddr addr;
socklen_t addrlen;
} connection_t;
int connection_init(connection_t* connection, const char* address, const uint16_t port) {
if (!port) {
if ((connection->socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
return -1;
}
memset(&connection->addr, 'x', sizeof(connection->addr));
((struct sockaddr_un*) & connection->addr)->sun_family = AF_UNIX;
((struct sockaddr_un*) & connection->addr)->sun_path[0] = '[=10=]';
strncpy(((struct sockaddr_un*) & connection->addr)->sun_path + 1, address, strlen(address));
connection->addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(address);
return 0;
}
}
int connetcion_connect(const connection_t* connection) {
if (connect(connection->socket, &connection->addr, connection->addrlen) == -1) {
return -1;
}
return 0;
}
int connection_send(const connection_t* connection, const char* buff) {
ssize_t len;
if ((len = send(connection->socket, buff, strlen(buff), 0)) == -1) {
return -1;
}
return len;
}
int connection_recv(const connection_t* connection, char** buff) {
ssize_t len;
if (((*buff) = (char*)malloc(sizeof(char) * (MSG_LEN + 1))) == NULL) {
return -1;
}
memset((*buff), 0, sizeof(char) * (MSG_LEN + 1));
len = recv(connection->socket, (*buff), MSG_LEN, 0);
if (len == -1 || len > MSG_LEN) {
free((*buff));
(*buff) = NULL;
return -1;
}
(*buff)[len] = 0;
/* Resize if string is shorter than len */
if (realloc((*buff), sizeof(char) * (strlen((*buff)) + 1)) == NULL) {
free((*buff));
(*buff) = NULL;
return -1;
}
return len;
}
int foo(char* query, char** result) {
connection_t con;
char* filename;
if (asprintf(&filename, "%s%s", getenv("HOME"), "/.directory/socket") == -1) {
return 1;
}
if (connection_init(&con, filename, 0) == -1) {
return 1;
}
free(filename);
if (connetcion_connect(&con) == -1) {
return 1;
}
if (connection_send(&con, query) == -1) {
return 1;
}
printf("Address %p in foo point to:\n", result);
printf("%p\n", *result);
if (connection_recv(&con, result) == -1) {
return 1;
}
printf("Address %p in foo point to:\n", result);
printf("%p\n", *result);
return 0;
}
int main(int argc, char *argv[]){
if (argc == 3 && !strncasecmp(argv[1], "hello", 5)) {
char* buff = NULL;
printf("Address %p in main point to:\n", &buff);
printf("%p\n", buff);
if(foo(argv[2], &buff)){
return 1;
}
printf("Address %p in main point to:\n", &buff);
printf("%p\n", buff);
printf("%s\n", buff);
}
return 0;
}
预期输出:
Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7ffd4c0c0d28" in main point to:
0x1bfa6a0
实际输出:
Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7f74656b6367" in main point to:
Segmentation fault
我能想到的唯一可能的解释是我以某种方式损坏了堆栈,如果需要其他资源请告诉我,我会尽快提供它们
附加信息:
尝试在 return 0
之前 printf("%s\n", *result)
我得到预期的字符串。
服务器使用相同的connection_*
功能与客户端通信。
我在 RHEL8.1 上,我正在用 gcc 8.3.1 编译它
您的 connection_t
只有一个普通的 struct sockaddr addr
成员,它只是基本结构。所需的实际数据大小因协议而异。当我 运行 你的代码时,con
分配在 ...29a0 并且有 24 个字节,但是 strncpy
试图将 41 个字节写入 ...29a7,因此超过 运行结构体。您需要为协议所需的特定结构分配内存。可能你的 addr
成员应该是一个指针,你应该为它分配内存,将指针设置为指向该内存,并调整你的其他代码。