【安全函数】memmove_s ():C 语言内存安全迁移的守护者与 memmove 深度对比

博主简介:byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发。深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域,乐于技术交流与分享。欢迎技术交流。

主页地址byte轻骑兵-CSDN博客

微信公众号:「嵌入式硬核研究所」

**邮箱:**byteqqb@163.com

声明:本文为「byte轻骑兵」原创文章,未经授权禁止任何形式转载。商业合作请联系作者授权。


在 C 语言内存操作的工具箱中,内存块迁移是一项基础而关键的操作。传统的 memmove () 函数虽然解决了内存重叠问题,却缺乏必要的安全检查机制,成为程序潜在漏洞的来源。C11 标准引入的 memmove_s () 函数,在保留 memmove () 核心功能的基础上,构建了全面的安全防护体系。


目录

一、函数简介

二、函数原型

三、函数实现(伪代码)

四、使用场景

五、注意事项

[六、示例代码:memmove_s () 实战应用](#六、示例代码:memmove_s () 实战应用)


一、函数简介

memmove_s () 是 C11 标准(ISO/IEC 9899:2011)定义的边界检查接口家族中的重要成员,其核心设计目标是在提供内存块安全迁移能力的同时,解决传统 memmove () 函数存在的安全隐患。它不仅继承了 memmove () 处理内存重叠的能力,更通过引入安全机制实现了对内存操作的全面保护。

1.1 安全与功能的双重突破

memmove_s () 的创新设计体现在两个关键维度:

  1. 增强的安全模型:在执行内存迁移前对所有输入参数进行严格验证,包括指针有效性、长度合理性和边界限制,从源头阻止缓冲区溢出等常见漏洞。

  2. 兼容的功能设计:保留了 memmove () 处理内存重叠的核心能力,确保在源内存块和目标内存块存在重叠时仍能正确复制数据,同时提供明确的错误处理机制。

1.2 与 memmove () 的本质差异

核心特性 memmove() memmove_s()
内存重叠处理 支持,可正确处理重叠区域 同样支持,算法与 memmove () 兼容
安全检查 无任何参数验证 全面检查空指针、长度溢出、边界超限
错误处理 未定义行为(通常崩溃或数据损坏) 返回错误码,可通过 errno 获取详情
边界控制 依赖调用者保证,无机制防护 通过目标最大尺寸参数强制控制
标准兼容性 C89 及以上 C11 及以上(需定义__STDC_WANT_LIB_EXT1__)
返回值 返回目标指针 返回错误码(0 表示成功)

这种设计差异使得 memmove_s () 特别适合以下场景:

  • 处理不可信来源的数据迁移(如用户输入、网络数据)
  • 安全关键型系统(如医疗设备、工业控制、自动驾驶)
  • 大型软件项目中的通用组件(减少人为失误风险)
  • 需要符合安全标准(如 ISO 26262、IEC 61508)的开发

在这些场景中,memmove_s () 通过将安全检查内置到函数中,有效降低了因调用者疏忽导致的安全风险。

二、函数原型

memmove_s () 的函数原型在保留 memmove () 核心功能的基础上,通过精心设计的参数体系实现了安全机制的落地,其标准定义如下:

cpp 复制代码
errno_t memmove_s(void *restrict dest, rsize_t destmax, 
                 const void *restrict src, rsize_t count);

2.1 参数解析与安全设计

参数 含义 安全作用 与 memmove () 差异
dest 目标内存块起始地址 指向待写入的内存区域 同 memmove () 的 dest 参数
destmax 目标内存块最大字节数 限制写入长度,防止溢出 memmove () 无此参数,缺乏边界控制
src 源内存块起始地址 指向待读取的内存区域 同 memmove () 的 src 参数
count 要迁移的字节数 指定迁移长度 对应 memmove () 的 n 参数,但增加有效性检查

2.2 返回值的安全语义

与 memmove () 返回目标指针不同,memmove_s () 返回 errno_t 类型的错误码,这是安全机制的关键体现:

  • 0:迁移成功完成
  • 非 0:发生错误,具体值对应不同错误类型

在 C11 标准中,规定了 memmove_s () 可能返回的错误码:

  • EINVAL:dest 或 src 为 NULL 指针
  • ERANGE:count > destmax(目标空间不足)
  • EOVERFLOW:count 或 destmax 超过 RSIZE_MAX(实现定义的最大限制)

这种设计强制调用者处理可能的错误情况,而不是像 memmove () 那样在出现问题时默默崩溃或产生错误结果。例如,当目标缓冲区容量不足时,memmove_s () 会返回 ERANGE 并避免执行可能导致溢出的操作。

三、函数实现(伪代码)

memmove_s () 的实现严格遵循 "安全检查优先" 的原则,在进行实际迁移前执行完整的参数验证,然后采用与 memmove () 兼容的算法处理内存迁移,包括重叠区域的正确处理。以下伪代码清晰展示了其执行流程:

cpp 复制代码
errno_t memmove_s(void *restrict dest, rsize_t destmax,
                 const void *restrict src, rsize_t count) {
    // 阶段1:参数安全验证(memmove()无此阶段)
    if (dest == NULL || src == NULL) {
        // 空指针检查
        set_errno(EINVAL);
        return EINVAL;
    }
    
    if (count > destmax) {
        // 目标空间不足检查
        set_errno(ERANGE);
        return ERANGE;
    }
    
    if (count > RSIZE_MAX || destmax > RSIZE_MAX) {
        // 超过实现定义的最大长度
        set_errno(EOVERFLOW);
        return EOVERFLOW;
    }
    
    // 阶段2:执行内存迁移(与memmove()核心逻辑相似)
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    if (d < s || d >= s + count) {
        // 目标在源之前,或无重叠:正向复制
        for (rsize_t i = 0; i < count; i++) {
            d[i] = s[i];
        }
    } else {
        // 有重叠:反向复制
        for (rsize_t i = count; i > 0; i--) {
            d[i - 1] = s[i - 1];
        }
    }
    
    return 0; // 成功
}

与 memmove () 实现的关键差异

memmove () 的典型实现如下,对比可见两者的本质区别:

cpp 复制代码
void *memmove(void *restrict dest, const void *restrict src, size_t n) {
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    if (d < s || d >= s + n) {
        // 正向复制
        for (size_t i = 0; i < n; i++) {
            d[i] = s[i];
        }
    } else {
        // 反向复制
        for (size_t i = n; i > 0; i--) {
            d[i - 1] = s[i - 1];
        }
    }
    
    return dest;
}

memmove_s () 的实现增加了三个关键安全层:

  1. 指针有效性验证:杜绝 NULL 指针解引用导致的崩溃,这是 memmove () 最常见的问题来源之一

  2. 边界检查:通过 destmax 参数确保迁移长度不超过目标内存容量,防止缓冲区溢出攻击

  3. 长度合理性校验:防止过大的 count 值导致的整数溢出,RSIZE_MAX 通常定义为 SIZE_MAX/2,避免长度计算时的溢出风险

这些检查虽然增加了约 10-15% 的性能开销(在多数场景可忽略),但彻底消除了 memmove () 固有的安全隐患,同时保留了处理内存重叠的核心能力。

四、使用场景

memmove_s () 与 memmove () 的使用场景有交集,但在安全敏感场景中,memmove_s () 是更优选择。通过具体场景对比,可清晰展现两者的适用边界。

1. 处理用户输入的缓冲区操作(memmove_s () 更优)

用户输入往往不可信,长度和内容均不可控,此时 memmove_s () 的安全检查至关重要:

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE 1024
#define __STDC_WANT_LIB_EXT1__ 1

// 安全处理用户输入的缓冲区操作
int process_user_input(const char *user_input, size_t input_len) {
    char buffer[BUFFER_SIZE];
    errno_t err;
    
    // 需要从输入中提取特定字段(偏移10字节开始的内容)
    if (input_len < 10) {
        printf("输入数据不完整\n");
        return -1;
    }
    
    size_t data_len = input_len - 10;
    
    // 使用memmove_s:自动检查data_len是否超过buffer容量
    err = memmove_s(buffer, BUFFER_SIZE, user_input + 10, data_len);
    if (err != 0) {
        printf("数据处理错误: %s\n", 
               err == ERANGE ? "缓冲区不足" : 
               err == EINVAL ? "无效参数" : "未知错误");
        return -1;
    }
    
    // 处理数据...
    printf("成功提取 %zu 字节数据\n", data_len);
    return 0;
}

// 使用memmove的不安全版本
int unsafe_process_user_input(const char *user_input, size_t input_len) {
    char buffer[BUFFER_SIZE];
    
    if (input_len < 10) return -1;
    size_t data_len = input_len - 10;
    
    // 无安全检查,若data_len > BUFFER_SIZE则导致缓冲区溢出
    memmove(buffer, user_input + 10, data_len);
    
    // 处理数据...
    return 0;
}

在这个场景中,memmove_s () 能自动检测并阻止 data_len 超过缓冲区大小的情况,而 memmove () 会默默执行溢出操作,可能导致程序崩溃或被利用为攻击入口。特别是当 user_input 来自不可信来源时,这种安全检查尤为重要。

2. 内存重叠场景的处理(两者皆可,安全层面 memmove_s () 更优)

内存重叠是常见场景,两种函数都能正确处理,但 memmove_s () 提供额外安全保障:

cpp 复制代码
#include <stdio.h>
#include <string.h>

// 安全处理内存重叠
void safe_handle_overlap() {
    char str[] = "abcdefghijklmnopqrstuvwxyz";
    printf("原始字符串: %s\n", str);
    
    // 将"fghij"移动到"bcdef"的位置(存在重叠)
    errno_t err = memmove_s(str + 1, sizeof(str) - 1, str + 5, 5);
    if (err == 0) {
        printf("安全移动后: %s\n", str); // 结果应为"afghijklmnopqrstuvwxyz"
    } else {
        printf("移动失败: %d\n", err);
    }
}

// 使用memmove处理内存重叠
void unsafe_handle_overlap() {
    char str[] = "abcdefghijklmnopqrstuvwxyz";
    printf("原始字符串: %s\n", str);
    
    // 同样处理重叠,但无安全检查
    memmove(str + 1, str + 5, 5);
    printf("普通移动后: %s\n", str); // 结果相同,但存在潜在风险
}

int main() {
    safe_handle_overlap();
    unsafe_handle_overlap();
    return 0;
}

两种函数都能正确处理内存重叠,但 memmove_s () 通过第二个参数 (sizeof (str) - 1) 确保移动操作不会超出目标缓冲区的边界,而 memmove () 则完全依赖调用者确保这一点。

3. 动态内存管理中的数据迁移(memmove_s () 降低风险)

动态内存分配中,大小计算错误时有发生,memmove_s () 能有效降低此类错误的影响:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define __STDC_WANT_LIB_EXT1__ 1

// 安全的动态内存迁移
void *safe_move_dynamic(const void *src, size_t src_size, size_t move_size) {
    // 分配目标内存
    void *dest = malloc(move_size);
    if (dest == NULL) return NULL;
    
    // 使用memmove_s确保迁移不会超过目标内存大小
    errno_t err = memmove_s(dest, move_size, src, move_size);
    if (err != 0) {
        free(dest);
        return NULL;
    }
    
    return dest;
}

// 不安全的动态内存迁移
void *unsafe_move_dynamic(const void *src, size_t move_size) {
    void *dest = malloc(move_size);
    if (dest == NULL) return NULL;
    
    // 无安全检查,若malloc失败或参数错误会导致问题
    memmove(dest, src, move_size);
    return dest;
}

int main() {
    int src[] = {1, 2, 3, 4, 5};
    size_t src_size = sizeof(src);
    
    // 迁移前3个元素(12字节)
    int *dest_safe = (int *)safe_move_dynamic(src, src_size, 3 * sizeof(int));
    if (dest_safe != NULL) {
        printf("安全迁移结果: %d, %d, %d\n", dest_safe[0], dest_safe[1], dest_safe[2]);
        free(dest_safe);
    }
    
    // 普通迁移
    int *dest_unsafe = (int *)unsafe_move_dynamic(src, 3 * sizeof(int));
    if (dest_unsafe != NULL) {
        printf("普通迁移结果: %d, %d, %d\n", dest_unsafe[0], dest_unsafe[1], dest_unsafe[2]);
        free(dest_unsafe);
    }
    
    return 0;
}

在动态内存场景中,memmove_s () 的安全检查可以捕获诸如目标内存分配失败(虽然这里通过 malloc 检查)、源指针无效等问题,而 memmove () 在这些情况下会导致未定义行为。

五、注意事项

memmove_s () 的安全机制带来了使用复杂度的增加,需特别注意以下事项,这也是与 memmove () 使用习惯的重要区别。

1. 编译器兼容性处理

memmove_s () 并非所有编译器都默认支持,需进行兼容性处理:

cpp 复制代码
// 跨编译器兼容的memmove_s使用方式
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>

#ifdef __STDC_LIB_EXT1__
// C11兼容环境,直接使用标准memmove_s
#define SAFE_MEMMOVE(dest, destmax, src, count) memmove_s(dest, destmax, src, count)
#else
// 非C11环境,使用自定义安全封装
errno_t safe_memmove_fallback(void *dest, size_t destmax, 
                            const void *src, size_t count) {
    if (dest == NULL || src == NULL || count > destmax) {
        return -1; // 模拟错误码
    }
    memmove(dest, src, count);
    return 0;
}
#define SAFE_MEMMOVE(dest, destmax, src, count) safe_memmove_fallback(dest, destmax, src, count)
#endif

主要编译器支持情况:

  • GCC 4.9+:需定义__STDC_WANT_LIB_EXT1__=1 并使用 - std=c11
  • Clang 3.6+:支持情况类似 GCC
  • MSVC 2013+:原生支持,但部分行为与标准略有差异
  • 嵌入式编译器:需查看具体版本的支持情况

在跨平台开发中,这种兼容性封装可以确保代码在不同环境下都能安全运行。

2. 错误码的正确处理

memmove_s () 的非零返回值必须处理,这是与 memmove () 使用习惯的最大不同:

cpp 复制代码
// 错误处理的最佳实践
void handle_move_result(errno_t err) {
    switch (err) {
        case 0:
            printf("内存迁移成功\n");
            break;
        case EINVAL:
            printf("错误:无效指针(NULL)\n");
            // 处理空指针情况,可能需要记录日志并返回错误状态
            break;
        case ERANGE:
            printf("错误:迁移长度超过目标容量\n");
            // 处理缓冲区不足,可能需要扩容或调整迁移策略
            break;
        case EOVERFLOW:
            printf("错误:长度超过系统最大限制\n");
            // 处理超大长度,可能是恶意输入
            break;
        default:
            printf("错误:未知错误(%d)\n", err);
            // 通用错误处理,确保程序稳定
    }
}

// 使用示例
void example() {
    char src[] = "hello world this is a long string";
    char dest[10];
    
    errno_t err = memmove_s(dest, sizeof(dest), src, strlen(src) + 1);
    handle_move_result(err); // 会报告"迁移长度超过目标容量"
}

与 memmove () 不同,使用 memmove_s () 时必须检查返回值,否则就失去了其安全特性的价值。良好的错误处理可以使程序在遇到问题时更加健壮。

3. destmax 参数的准确设置

destmax 必须准确反映目标内存的实际容量,这是安全机制的核心:

cpp 复制代码
// 正确与错误的destmax设置对比
void destmax_examples() {
    int src[5] = {1, 2, 3, 4, 5};
    int dest[3]; // 容量为3个int(12字节)
    
    // 正确:destmax设置为实际容量
    memmove_s(dest, sizeof(dest), src, 2 * sizeof(int)); // 成功
    
    // 错误1:destmax设置过大
    memmove_s(dest, 100, src, 5 * sizeof(int)); // 实际迁移会超过dest容量,返回ERANGE
    
    // 错误2:destmax设置为元素数而非字节数
    memmove_s(dest, 3, src, 2 * sizeof(int)); // 3字节 < 8字节,返回ERANGE
}

destmax 参数的单位是字节,这一点与 count 参数一致,初学者常错误地使用元素数量而非字节数,导致不必要的错误。

4. 与其他安全函数的配合使用

在实际开发中,memmove_s () 常与其他安全函数配合使用,形成完整的安全内存操作体系:

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define __STDC_WANT_LIB_EXT1__ 1

// 安全字符串处理示例
char *safe_substring(const char *str, size_t start, size_t length) {
    if (str == NULL) return NULL;
    
    size_t str_len = strlen(str);
    if (start >= str_len) return NULL;
    
    size_t actual_length = (start + length > str_len) ? (str_len - start) : length;
    
    // 使用安全函数分配内存
    char *result = (char *)malloc(actual_length + 1);
    if (result == NULL) return NULL;
    
    // 使用memmove_s迁移数据
    errno_t err = memmove_s(result, actual_length + 1, str + start, actual_length);
    if (err != 0) {
        free(result);
        return NULL;
    }
    
    // 使用安全函数设置终止符
    result[actual_length] = '\0';
    return result;
}

示例展示了 memmove_s () 与 malloc () 配合使用的安全模式,通过错误检查确保每个步骤的安全性。

六、示例代码:memmove_s () 实战应用

以下通过完整示例展示 memmove_s () 在实际开发中的应用,对比 memmove () 的实现,凸显安全特性。

示例 1:安全的缓冲区管理库

实现一个安全的缓冲区管理库,支持数据插入、删除和迁移操作:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define __STDC_WANT_LIB_EXT1__ 1

// 安全缓冲区结构体
typedef struct {
    char *data;       // 数据指针
    size_t capacity;  // 总容量
    size_t length;    // 当前长度
} SafeBuffer;

// 创建安全缓冲区
SafeBuffer *safe_buffer_create(size_t capacity) {
    if (capacity == 0) return NULL;
    
    SafeBuffer *buf = (SafeBuffer *)malloc(sizeof(SafeBuffer));
    if (buf == NULL) return NULL;
    
    buf->data = (char *)malloc(capacity);
    if (buf->data == NULL) {
        free(buf);
        return NULL;
    }
    
    buf->capacity = capacity;
    buf->length = 0;
    return buf;
}

// 向缓冲区插入数据
int safe_buffer_insert(SafeBuffer *buf, size_t pos, 
                      const void *data, size_t data_len) {
    if (buf == NULL || data == NULL || pos > buf->length) {
        return -1;
    }
    
    // 检查是否有足够空间
    if (buf->length + data_len > buf->capacity) {
        return -2; // 空间不足
    }
    
    // 移动现有数据为新数据腾出空间(可能有重叠)
    if (buf->length > pos) {
        errno_t err = memmove_s(buf->data + pos + data_len, 
                               buf->capacity - (pos + data_len),
                               buf->data + pos, 
                               buf->length - pos);
        if (err != 0) {
            return -3; // 移动失败
        }
    }
    
    // 复制新数据
    errno_t err = memmove_s(buf->data + pos, 
                           buf->capacity - pos,
                           data, 
                           data_len);
    if (err != 0) {
        // 恢复之前的移动操作(简化处理,实际可能更复杂)
        if (buf->length > pos) {
            memmove_s(buf->data + pos, 
                     buf->capacity - pos,
                     buf->data + pos + data_len, 
                     buf->length - pos);
        }
        return -4; // 复制失败
    }
    
    buf->length += data_len;
    return 0; // 成功
}

// 从缓冲区删除数据
int safe_buffer_delete(SafeBuffer *buf, size_t pos, size_t delete_len) {
    if (buf == NULL || pos >= buf->length || pos + delete_len > buf->length) {
        return -1;
    }
    
    // 移动数据覆盖被删除的部分(可能有重叠)
    if (pos + delete_len < buf->length) {
        errno_t err = memmove_s(buf->data + pos, 
                               buf->capacity - pos,
                               buf->data + pos + delete_len, 
                               buf->length - (pos + delete_len));
        if (err != 0) {
            return -2; // 移动失败
        }
    }
    
    buf->length -= delete_len;
    return 0; // 成功
}

// 销毁缓冲区
void safe_buffer_destroy(SafeBuffer *buf) {
    if (buf) {
        free(buf->data);
        free(buf);
    }
}

// 测试缓冲区操作
int main() {
    SafeBuffer *buf = safe_buffer_create(100);
    if (!buf) {
        printf("创建缓冲区失败\n");
        return 1;
    }
    
    // 插入初始数据
    const char *init_data = "Hello, World!";
    if (safe_buffer_insert(buf, 0, init_data, strlen(init_data)) != 0) {
        printf("插入初始数据失败\n");
        safe_buffer_destroy(buf);
        return 1;
    }
    printf("初始数据: %.*s\n", (int)buf->length, buf->data);
    
    // 插入新数据
    const char *insert_data = " beautiful";
    if (safe_buffer_insert(buf, 6, insert_data, strlen(insert_data)) != 0) {
        printf("插入数据失败\n");
        safe_buffer_destroy(buf);
        return 1;
    }
    printf("插入后: %.*s\n", (int)buf->length, buf->data);
    
    // 删除部分数据
    if (safe_buffer_delete(buf, 6, strlen(insert_data)) != 0) {
        printf("删除数据失败\n");
        safe_buffer_destroy(buf);
        return 1;
    }
    printf("删除后: %.*s\n", (int)buf->length, buf->data);
    
    safe_buffer_destroy(buf);
    return 0;
}

示例展示了 memmove_s () 在缓冲区管理中的应用,通过安全的数据迁移确保插入和删除操作不会导致缓冲区溢出,同时正确处理内存重叠情况。与使用 memmove () 的实现相比,这个版本能更好地抵御错误输入和恶意攻击。

示例 2:安全的网络数据包重组

实现一个安全的网络数据包重组器,处理可能的恶意分片:

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define __STDC_WANT_LIB_EXT1__ 1
#define MAX_PACKET_SIZE 1500
#define MAX_FRAGMENTS 10

// 数据包分片结构
typedef struct {
    uint8_t fragment_id;    // 分片ID
    uint16_t offset;        // 偏移量
    uint16_t length;        // 长度
    uint8_t data[MAX_PACKET_SIZE]; // 数据
} PacketFragment;

// 完整数据包结构
typedef struct {
    uint8_t data[MAX_PACKET_SIZE * MAX_FRAGMENTS];
    size_t total_length;
    uint8_t fragments_received;
    uint8_t expected_fragments;
} CompletePacket;

// 初始化完整数据包
void init_complete_packet(CompletePacket *packet, uint8_t expected_fragments) {
    if (packet == NULL) return;
    
    memset(packet->data, 0, sizeof(packet->data));
    packet->total_length = 0;
    packet->fragments_received = 0;
    packet->expected_fragments = expected_fragments;
}

// 添加分片到完整数据包
int add_fragment(CompletePacket *packet, const PacketFragment *fragment) {
    if (packet == NULL || fragment == NULL) {
        return -1; // 无效参数
    }
    
    // 检查分片是否超出预期
    if (packet->fragments_received >= packet->expected_fragments) {
        return -2; // 已接收所有预期分片
    }
    
    // 检查偏移量和长度是否合理
    if (fragment->offset + fragment->length > sizeof(packet->data)) {
        return -3; // 分片超出缓冲区
    }
    
    // 安全地将分片数据移动到完整数据包(可能有重叠)
    errno_t err = memmove_s(packet->data + fragment->offset,
                           sizeof(packet->data) - fragment->offset,
                           fragment->data,
                           fragment->length);
    if (err != 0) {
        return -4; // 数据移动失败
    }
    
    // 更新数据包信息
    packet->fragments_received++;
    if (fragment->offset + fragment->length > packet->total_length) {
        packet->total_length = fragment->offset + fragment->length;
    }
    
    return 0; // 成功
}

// 测试数据包重组
int main() {
    CompletePacket packet;
    init_complete_packet(&packet, 3); // 预期3个分片
    
    // 创建测试分片
    PacketFragment frag1 = {1, 0, 5, {'H', 'e', 'l', 'l', 'o'}};
    PacketFragment frag2 = {2, 5, 7, {' ', 'W', 'o', 'r', 'l', 'd', '!'}};
    PacketFragment frag3 = {3, 12, 4, {' ', '1', '2', '3'}};
    
    // 添加分片
    if (add_fragment(&packet, &frag1) != 0) printf("添加分片1失败\n");
    if (add_fragment(&packet, &frag2) != 0) printf("添加分片2失败\n");
    if (add_fragment(&packet, &frag3) != 0) printf("添加分片3失败\n");
    
    // 检查是否接收完成
    if (packet.fragments_received == packet.expected_fragments) {
        printf("数据包重组完成: %.*s\n", (int)packet.total_length, packet.data);
    }
    
    // 测试恶意分片(过大的长度)
    PacketFragment malicious_frag = {4, 0, MAX_PACKET_SIZE * 2, {0}};
    int result = add_fragment(&packet, &malicious_frag);
    if (result == -3) {
        printf("成功拦截恶意分片\n");
    }
    
    return 0;
}

memmove_s () 用于安全地将分片数据移动到完整数据包中,通过检查目标缓冲区大小防止恶意分片导致的缓冲区溢出。与使用 memmove () 的实现相比,这种方法能更好地处理不可信的网络输入。


memmove_s () 作为 memmove () 的安全升级版本,代表了 C 语言在安全性方面的重要进步。

1. 安全价值:memmove_s () 通过参数验证、边界控制和错误处理三大机制,在保留处理内存重叠能力的同时,彻底解决了 memmove () 的安全隐患,特别适合处理不可信数据。

2. 使用原则

  • 新代码优先使用 memmove_s (),尤其是处理外部输入时
  • 性能敏感且内部可控的场景可保留 memmove ()
  • 始终处理 memmove_s () 的返回值,不忽略错误检查
  • 正确设置 destmax 参数,确保反映目标缓冲区的实际容量

3. 兼容性策略:在不支持 C11 的环境中,应实现安全封装函数,模拟 memmove_s () 的安全特性

4. 局限性:memmove_s () 的安全检查带来一定性能开销(通常可接受),且需要显式处理错误码,增加了代码复杂度

在软件安全日益重要的今天,从 memmove () 到 memmove_s () 的转变,不仅是函数的替换,更是编程理念的升级 ------ 将安全从 "开发者责任" 转变为 "机制保障"。掌握 memmove_s () 的使用,是每个 C 语言开发者提升代码质量、构建安全系统的必备技能。通过合理使用这些安全函数,我们可以在不牺牲性能的前提下,显著提高软件的安全性和可靠性。


相关推荐
用户962377954485 小时前
DVWA 靶场实验报告 (High Level)
安全
RuoZoe7 小时前
重塑WPF辉煌?基于DirectX 12的现代.NET UI框架Jalium
c语言
数据智能老司机8 小时前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机8 小时前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户9623779544810 小时前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star10 小时前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户9623779544813 小时前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
祈安_4 天前
C语言内存函数
c语言·后端
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php