7

软安课程作业1-编写安全的strcpy

 1 year ago
source link: https://suyumen.github.io/2022/10/10/2022-10-10-%E8%BD%AF%E5%AE%89%E8%AF%BE%E7%A8%8B%E4%BD%9C%E4%B8%9A1-%E7%BC%96%E5%86%99%E5%AE%89%E5%85%A8%E7%9A%84strcpy/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

软安课程作业1-编写安全的strcpy

Word count: 1.1k | Reading time: 4min

1、作业内容

  • (1)理解常见的字符串操作错误;
  • (2)掌握字符串错误的缓解措施;
  • (3)编写一个安全的strcpy函数,能够达到如下要求:
    • a)不会产生缓冲区溢出
    • b)不产生无结尾的字符串
    • c)不意外截断字符串
  • (4)参考函数定义如下:
    errno_t strcpy_s(char * restrict s1,rsize_t s1max,const char * restrict s2);
  • (5)该函数能够发现的字符串操作错误包括:
    • a)无界字符串复制
    • b)空结尾错误
    • d)差-错误

2、函数编写思路

图1  mystrcpy_s函数流程图
  • 函数原型及参数解释:

    /**
     * @brief: 安全strcpy()
     * @param: s1-目的字符串指针 s2-复制源字符串指针 s1max-目的地址可用长度
     * @ret: 返回错误类型
     */
    errno_t mystrcpy_s(char *__restrict s1, rsize_t s1max, const char *__restrict s2)
  • 函数设计思路:

    • 无边界字符串错误和字符串截断错误检测:
      如果无界字符串的长度短于目的地址可用长度,相当于合法情况下的字符串复制;如果无界字符串的长度长于目的地址可用长度,此时与字符串截断错误类似,在函数内都表现为接收到待复制的字符串长于目的地址(因此我也没有想法在此情景下区分无界字符串错误和字符串截断错误,只能判断可能是其中之一,具体是哪种错误要看传参字符串是否通过无边界方式得到)。
    • 空结尾错误检测:
      在目的地址可用长度刚好等于待复制字符串长度(不包括0结尾)时,此时检测到没有可分配的空间在结尾补充0,判定可能是空结尾错误。
    • 差一错误检测:
      在该函数情景下,认为差一错误可能来自参数的设置不当,形如
      char d[strlen(e)];
      mystrcpy_s(d, strlen(e), e);
      而不是
      char d[sizeof(e)];
      mystrcpy_s(d, sizeof(e), e);
      此时认为可能是差一错误。(但其参数表示情况和空结尾错误相似,因此我没法区分是这两种错误中的哪一种。)
    • 缓冲区溢出:
      通过参数控制静态缓冲区大小。

      3、源码展示

  • Main.cpp 测试文件

    #include "mystrcpy_s.h"
    #include <cstring>
    #include <iostream>

    int main(int argc, char *argv[])
    {
        char a[10];
        mystrcpy_s(a, 10, argv[1]);
        std::cout << a << std::endl;//无边界字符串复制

    char b[10];
        strncpy(b, "0123456789", sizeof(b));
        char c[10];
        mystrcpy_s(c, 10, b);
        std::cout << c << std::endl; //空结尾错误

    char e[] = "0123456789";
        char d[strlen(e)];
        mystrcpy_s(d, strlen(e), e);
        std::cout << d << std::endl; //差一错误

    char f[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        char g[10];
        mystrcpy_s(g, 10, f);
        std::cout << g << std::endl;//字符串截断错误

    char h[] = "correct";
        char i[sizeof(h) + 1];
        mystrcpy_s(i, sizeof(i), h);
        std::cout << i << std::endl;

    system("pause");
    }
  • Mystrcpy_s.cpp 函数实现

    #include <stdio.h>
    #include <iostream>
    #define _DEBUG

    /**
     * @brief: 安全strcpy()
     * @param: s1-目的字符串指针 s2-复制源字符串指针 s1max-目的地址可用长度
     * @ret: 返回错误类型
     */
    errno_t mystrcpy_s(char *__restrict s1, rsize_t s1max, const char *__restrict s2)
    {
        char *p;
        rsize_t max;
        for (p = s1, max = s1max; *s2 != 0 && max > 0; *p++ = *s2++, max--);
        if (max == 0 && *s2 == 0)
        {
            *s1 = 0;
    #ifdef _DEBUG
            std::cout << "[off-by-one error] or [null termination error]" << std::endl;
    #endif
            return ERANGE;
        }
        if (max == 0)
        {
            *s1 = 0;
    #ifdef _DEBUG
            std::cout << "[string truncation error] or [unbounded string copy]" << std::endl;
    #endif
            return EINVAL;
        }
        *p = 0;
        return 0;
    }
  • Mystrcpy_s.h 函数声明

    #ifndef MYSTRCPY_S_H
    #define MYSTRCPY_S_H
    #include <stdio.h>
    #include <iostream>
    errno_t mystrcpy_s(char *__restrict, rsize_t, const char *__restrict);

    #endif

4、程序演示

程序运行结果。

图2 程序运行结果

5、遇到的问题及解决办法

本来想对缓冲区大小作限制,但在运行以下代码时产生错误。

errno_t mystrcpy_s(char *__restrict s1, rsize_t s1max, const char *__restrict s2)
{
std::cout << sizeof(s1);

在函数内打印sizeof(s1)发现不管在外面创建的时候是多少,打印出来的都只是8,但是在主函数里是正确的数组大小。思考了一下想起8是64位的指针大小,而sizeof判断类型大小,对于函数内部,传入的就是一个指针罢了,因此才总是8。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK