【安全函数】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 语言开发者提升代码质量、构建安全系统的必备技能。通过合理使用这些安全函数,我们可以在不牺牲性能的前提下,显著提高软件的安全性和可靠性。


相关推荐
秋邱2 小时前
Java抽象类与接口的核心区别:定义、特性与选型逻辑全解析
java·开发语言
ly_Enhs2 小时前
Vulkan 一句话心智词典(去恐惧版)
开发语言·vulkan图形渲染c/c++
成为大佬先秃头2 小时前
渐进式JavaScript框架:Vue 工具 & 模块化 & 迁移
开发语言·javascript·vue.js
程序员小白条2 小时前
提前实习的好处有哪些?有坏处吗?
java·开发语言·数据结构·数据库·链表
ss2732 小时前
Executors预定义线程池-正确使用姿势
linux·开发语言·python
七夜zippoe2 小时前
Python高级数据结构深度解析:从collections模块到内存优化实战
开发语言·数据结构·python·collections·内存视图
lly2024062 小时前
Vue.js 过渡 & 动画
开发语言
石工记2 小时前
Java 作为主开发语言 + 调用 AI 能力(大模型 API / 本地化轻量模型)
java·开发语言·人工智能
Ccuno2 小时前
Java虚拟机的内存结构
java·开发语言·深度学习