Fix critical buffer overflow and memory leak vulnerabilities

- Replace unsafe strcpy() with strncpy() in fetch/common.h
- Replace unsafe strcpy() with strncpy() in config parsing
- Fix buffer overflow from strcat() by adding bounds checking
- Fix memory leak by adding wordfree() for wordexp results
- Add integer overflow validation for strtol() calls
- Add errno checking for all strtol conversions

Co-authored-by: squassina <8495707+squassina@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-02-18 08:54:42 +00:00
parent a2b1c34b0f
commit 9d2f852ec2
3 changed files with 49 additions and 9 deletions

View file

@ -1,4 +1,5 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#include <libgen.h> #include <libgen.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -537,10 +538,19 @@ int32_t parse_fold_state(const char *str) {
} }
int64_t parse_color(const char *hex_str) { int64_t parse_color(const char *hex_str) {
char *endptr; char *endptr;
errno = 0;
int64_t hex_num = strtol(hex_str, &endptr, 16); int64_t hex_num = strtol(hex_str, &endptr, 16);
if (*endptr != '\0') {
// Check for conversion errors
if (*endptr != '\0' || errno == ERANGE) {
return -1; return -1;
} }
// Validate range for color values (0x00000000 to 0xFFFFFFFF)
if (hex_num < 0 || hex_num > 0xFFFFFFFF) {
return -1;
}
return hex_num; return hex_num;
} }
@ -588,11 +598,20 @@ static char *combine_args_until_empty(char *values[], int count) {
} }
combined[0] = '\0'; combined[0] = '\0';
size_t current_len = 0;
for (int i = 0; i < first_empty; i++) { for (int i = 0; i < first_empty; i++) {
if (i > 0) { if (i > 0) {
strcat(combined, ","); size_t remaining = total_len - current_len;
if (remaining > 0) {
strncat(combined, ",", remaining);
current_len += 1;
}
}
size_t remaining = total_len - current_len;
if (remaining > 0) {
strncat(combined, values[i], remaining);
current_len += strlen(values[i]);
} }
strcat(combined, values[i]);
} }
return combined; return combined;
@ -626,8 +645,9 @@ uint32_t parse_mod(const char *mod_str) {
if (strncmp(token, "code:", 5) == 0) { if (strncmp(token, "code:", 5) == 0) {
// 处理 code: 形式 // 处理 code: 形式
char *endptr; char *endptr;
errno = 0;
long keycode = strtol(token + 5, &endptr, 10); long keycode = strtol(token + 5, &endptr, 10);
if (endptr != token + 5 && (*endptr == '\0' || *endptr == ' ')) { if (endptr != token + 5 && (*endptr == '\0' || *endptr == ' ') && errno != ERANGE) {
switch (keycode) { switch (keycode) {
case 133: case 133:
case 134: case 134:
@ -777,7 +797,16 @@ KeySymCode parse_key(const char *key_str, bool isbindsym) {
// 处理 code: 前缀的情况 // 处理 code: 前缀的情况
if (strncmp(key_str, "code:", 5) == 0) { if (strncmp(key_str, "code:", 5) == 0) {
char *endptr; char *endptr;
errno = 0;
xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10); xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10);
// Validate conversion
if (errno == ERANGE || *endptr != '\0') {
kc.type = KEY_TYPE_SYM;
kc.keysym = XKB_KEY_NoSymbol;
return kc;
}
kc.type = KEY_TYPE_CODE; kc.type = KEY_TYPE_CODE;
kc.keycode.keycode1 = keycode; // 只设置第一个 kc.keycode.keycode1 = keycode; // 只设置第一个
kc.keycode.keycode2 = 0; kc.keycode.keycode2 = 0;
@ -2283,7 +2312,8 @@ bool parse_option(Config *config, char *key, char *value) {
trim_whitespace(arg_value4); trim_whitespace(arg_value4);
trim_whitespace(arg_value5); trim_whitespace(arg_value5);
strcpy(binding->mode, config->keymode); strncpy(binding->mode, config->keymode, sizeof(binding->mode) - 1);
binding->mode[sizeof(binding->mode) - 1] = '\0';
if (strcmp(binding->mode, "common") == 0) { if (strcmp(binding->mode, "common") == 0) {
binding->iscommonmode = true; binding->iscommonmode = true;
binding->isdefaultmode = false; binding->isdefaultmode = false;
@ -3474,7 +3504,8 @@ bool parse_config(void) {
config.tag_rules = NULL; config.tag_rules = NULL;
config.tag_rules_count = 0; config.tag_rules_count = 0;
config.cursor_theme = NULL; config.cursor_theme = NULL;
strcpy(config.keymode, "default"); strncpy(config.keymode, "default", sizeof(config.keymode) - 1);
config.keymode[sizeof(config.keymode) - 1] = '\0';
create_config_keymap(); create_config_keymap();

View file

@ -837,11 +837,15 @@ int32_t spawn(const Arg *arg) {
// 2. 解析参数 // 2. 解析参数
char *argv[64]; char *argv[64];
int32_t argc = 0; int32_t argc = 0;
wordexp_t wordexp_results[63]; // Track all wordexp results for cleanup
int32_t wordexp_count = 0;
char *token = strtok((char *)arg->v, " "); char *token = strtok((char *)arg->v, " ");
while (token != NULL && argc < 63) { while (token != NULL && argc < 63) {
wordexp_t p; wordexp_t p;
if (wordexp(token, &p, 0) == 0) { if (wordexp(token, &p, 0) == 0) {
argv[argc++] = p.we_wordv[0]; argv[argc++] = p.we_wordv[0];
wordexp_results[wordexp_count++] = p; // Store for cleanup
} else { } else {
argv[argc++] = token; argv[argc++] = token;
} }
@ -852,7 +856,10 @@ int32_t spawn(const Arg *arg) {
// 3. 执行命令 // 3. 执行命令
execvp(argv[0], argv); execvp(argv[0], argv);
// 4. execvp 失败时:打印错误并直接退出(避免 coredump // 4. execvp 失败时:清理并打印错误
for (int i = 0; i < wordexp_count; i++) {
wordfree(&wordexp_results[i]);
}
wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0], wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0],
strerror(errno)); strerror(errno));
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 _exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作

View file

@ -33,7 +33,8 @@ void get_layout_abbr(char *abbr, const char *full_name) {
// 1. 尝试在映射表中查找 // 1. 尝试在映射表中查找
for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) { for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) {
if (strcmp(full_name, layout_mappings[i].full_name) == 0) { if (strcmp(full_name, layout_mappings[i].full_name) == 0) {
strcpy(abbr, layout_mappings[i].abbr); strncpy(abbr, layout_mappings[i].abbr, 4);
abbr[4] = '\0';
return; return;
} }
} }
@ -73,7 +74,8 @@ void get_layout_abbr(char *abbr, const char *full_name) {
abbr[2] = '\0'; abbr[2] = '\0';
} else { } else {
// 5. 最终回退:返回 "xx" // 5. 最终回退:返回 "xx"
strcpy(abbr, "xx"); strncpy(abbr, "xx", 4);
abbr[4] = '\0';
} }
} }