C/C++使用过程中的溢出问题

内容:在C/C++程序里有一类非常典型的问题,那就是:溢出问题。现在分别来分析一下常见的数组溢出,整数溢出,缓冲区溢出,栈溢出和指针溢出等。

目录

1、数组溢出

2、整数溢出

3、缓冲区溢出

4、栈溢出

5、指针溢出

6、字符串溢出


1、数组溢出

在C语言中,数组的元素下标是从0开始计算的,所以,对于n个元素的数组a[n], 遍历它的时候是a[0],a[1],...,a[n-1],如果遍历到a[n],数组就溢出了。 
void print_array(int a[], int n)
{
    for (int i = 0; i < n; i++) 
    {
        a[i] = a[i+1];//当i = n-1时,就发生了数组越界
        printf(“%d\n”, a[i]);
    }
}
上面的循环判断应该改为:
for (int i = 0; i < n-1; i++)

2、整数溢出

整数的溢出分为下溢出和上溢出。比如,对于有符号的char(signed char)类型来说,它能表示的范围为:[-128,127]之间;而对于无符号的char(unsigned char)来说, 它能表示的范围为:[0,255]。
那么,对于下面的代码:
signed char c1 = 127;
c1 = c1+1;//发生上溢出,c1的值将变为-128
signed char c2 = -128;
c2 = c2-1;//发生下溢出,c2的值将变为127
unsigned char c3 = 255;
c3 = c3+1;//发生上溢出,c3的值将变为0
unsigned char c4 = 0;
c4 = c4-1;//发生下溢出,c4的值将变为255
从上面的例子可以看出,当一个整数向上溢出,将会变为最小值,而向下溢出,将会变为最大值。

来看下面的溢出代码,该代码负责提供一个小写字母转换表,但存在一个整数溢出问题:
void BuildToLowerTable( void ) /* ASCII版本*/
{
    unsigned char ch;
    /* 首先将每个字符置为它自己 */
    /*ch为unsigned char,无符号数,当ch值为UCHAR_MAX, ch++将会发生向上溢出,变为0,导致循环无法退出。*/
    for (ch=0; ch <= UCHAR_MAX;ch++)
        chToLower[ch] = ch;
    /* 将大写字母改为小写字母 */
    for( ch = ‘A’; ch <= ‘Z’; ch++ )
        chToLower[ch] = ch +’a’ – ‘A’;
}
该代码负责在内存中查找指定的字符ch,但也存在一个溢出问题
void * memchr( void *pv, unsigned char ch, size_t size )
{
    unsigned char *pch = (unsigned char *) pv;
    /*当size的值为0的时候,由于size是无符号整数,因此会发生下溢出,变为一个最大的整数 循环也将无法退出*/ 
    while( -- size >=0 )
    {
        if( *pch == ch )
            return (pch );
        pch++;
    }
    return( NULL );
}

3、缓冲区溢出

缓冲区溢出一般是调用了一些不安全的字符串操作函数比如:strcpy,strcat等(这些字符串操作函数在拷贝或者修改目标位置的时候,并不判断长度是否会超过目标缓存),或者设置参数超过了目标缓存能容纳的大小而造成的溢出问题。
void func1(char* s)
{
    char buf[10];
    /*此时,buf只有10个字节,如果传入的s超过10个字节,就会造成溢出*/
    strcpy(buf, s);
}
void func2(void)
{
    printf("Hacked by me.\n");
    exit(0);
}
int main(int argc, char* argv[])
{
    char badCode[] = "aaaabbbb2222cccc4444ffff";
    DWORD* pEIP = (DWORD*)&badCode[16];
    *pEIP = (DWORD)func2;
    /*badCode字符串超过了10个字节,传递给func1会造成栈上缓冲区溢出
    而且,由于badCode经过精心构造,在溢出的时候,根据函数的调用约定规则,会覆盖栈上的返回地址,
    指向了func2。所以,在func1退出的时候,会直接调用func2
    */
    func1(badCode);
    return 0;
}

4、栈溢出


无论是内核栈,还是应用层的栈,都是有一定大小限制的。如果在栈上分配的空间大于了这个限制,就会造成栈大小溢出,破坏栈上的数据。比如局部变量过多,或者递归调度嵌套太深都会造成栈溢出。比如:
int init_module(void)
{
    char buf[10000]; //buf[]分配在栈上,但10000的空间超过了栈的默认大小8KB。
    //所以发生溢出
    memset(buf,0,10000);
    printk("kernel stack.\n");
    return 0;
}
void cleanup_module(void)

    printk("goodbye.\n");
}
MODULE_LICENSE("GPL");
//应用栈的大小对少?内核栈的大小多少?什么时候容易栈溢出?

5、指针溢出

一块长度为size大小的内存buffer,buffer的首地址为p,那么buffer最后一个字节的地址:
p+size-1,而不是p+size。如果写成了p+size,就会造成溢出,比如下面的代码:
void* memchr( void *pv, unsigned char ch, size_t size )
{
    unsigned char *pch = ( unsigned char * )pv;
    unsigned char *pchEnd = pch + size;
    while( pch < pchEnd )
    {
        if( *pch == ch )
            return ( pch );
        pch ++ ;
    }
    return( NULL );
}

上面的代码用于查找内存中特定的字符位置。对于其中的while()循环,平时执行似乎都没有任何问题。但是,考虑一种特别情况,即pv所指的内存位置为末尾若干字节,那么因为pchEnd = pch+size,所以pchEnd指向最后一个字符的下一个字节,将会超出内存的范围,即pchEnd所指的位置已经不存在。
知道了问题所在,那么可以将内存的结尾计算方式改为: 
pchEnd = pv + size – 1; 
while ( pch <= pchEnd ) 
{
        if( *pch == ch )
            return ( pch );
        pch ++ ;
}
…… 
pchEnd指向了最后一个字节。但是,检查循环内部的执行情况可知,由于pch每增加到pchEnd+1时,都会发生上溢。因此,循环将无法退出。 于是,可以将程序修改为下面的代码。将用size变量来控制循环的退出。这样就不会存在任何问题了。
void *memchr( void *pv, unsigned char ch, size_t size )
{
    unsigned char *pch = ( unsigned char * )pv;
    while( size -- > 0 )
    {
        if( *pch == ch )
            return( pch );
        pch ++;
    }
    return( NULL );
}

大家知道,--size的效率一般比size--的效率高。那么是否可以将循环的判断条件改为下面的语句呢? 
while( --size >= 0 ) 
…… 

实际上这是不行的。因为当size=0时,由于size是无符号数,那么它将发生下溢,变成了size所能表示的最大正数,循环也将无法退出。 

6、字符串溢出

我们已经知道,字符串是'\0'结尾的。如果字符串结尾忘记带上'\0',那么就溢出了。注意,strlen(p)计算的是字符串中有效的字符数(不含’\0’)。考察下面拷贝字符串的代码,看看有什么问题没呢?

char *str = “Hello, how are you!”;

char *strbak = (char *)malloc(strlen(str));

if (NULL == strbak)

{

//处理内存分配失败,返回错误

}

strcpy(strbak, str);

......

显然,由于strlen()计算的不是str的实际长度(即不包含’\0’字符的计算),所以strbak没有结束符’\0’,而在C语言中,’\0’是字符串的结束标志,所以是必须加上的,否则会造成字符串的溢出。所以上面的代码应该是:

char *str = “Hello, how are you!”;

char *strbak = (char *)malloc(strlen(str)+1);

if (NULL == strbak)

{

    //内存分配失败,返回错误

}

strcpy(strbak, str);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/605617.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【docker笔记】DockerFile

DockerFile Docker镜像结构的分层 镜像不是一个单一的文件&#xff0c;而是有多层构成。 容器其实是在镜像的最上面加了一层读写层&#xff0c;在运行容器里做的任何文件改动&#xff0c;都会写到这个读写层。 如果删除了容器&#xff0c;也就是删除了其最上面的读写层&…

科技感十足界面模板

科技感界面 在强调简洁的科技类产品相关设计中&#xff0c;背景多数分为&#xff1a;颜色或写实图片两种。 颜色很好理解&#xff0c;大多以深色底为主。强调一种神秘感和沉稳感&#xff0c;同时可以和浅色的文字内容形成很好的对比。 而图片背景的使用&#xff0c;就要求其…

Java Stream:让你的集合操作如丝般顺滑

Stream API是一种基于流&#xff08;stream&#xff09;的API&#xff0c;它提供了一种简洁而高效的处理集合和数组的方法。使用Stream API可以将集合和数组中的数据进行过滤、转换和聚合等操作&#xff0c;同时避免了使用传统的for循环或迭代器的冗长代码. kotlin 可参考Kotl…

构建高效PythonWeb:GraphQL+Sanic

1.1 简介&#xff1a;在当今快速发展的技术时代&#xff0c;Web应用的性能和灵活性变得越来越重要。在众多技术中&#xff0c;GraphQL和Sanic以其独特的优势脱颖而出。GraphQL&#xff0c;作为一个强大的数据查询语言&#xff0c;为前端和后端之间的通信提供了极大的灵活性。而…

【现代密码学】笔记2 -- 完善保密性《introduction to modern cryphtography》现代密码学原理与协议

【现代密码学】笔记2--完善保密性《introduction to modern cryphtography》 写在最前面2 完善保密性的介绍2.1 定义和基本属性加密方案的组成密钥产生算法 (Gen)加密算法 (Enc)解密算法 (Dec)概率分布独立性 完美保密加密3. 回顾加密词法4. 完美保密&#xff08;**Perfect Sec…

docker打包介绍

最近在做一个开源项目&#xff0c;遇到开发者问各种问题&#xff0c;发现都是系统和软件版本的差异引起的。于是了解了一下docker的使用&#xff0c;发现docker真是个好东东&#xff0c;基本解决了各种版本差异的问题&#xff0c;真正做到了一键部署使用。 先熟悉一下docker里…

阿里云2核4G服务器ecs.e-c1m2.large价格和性能测评

2024年阿里云2核4G服务器优惠价格30元3个月&#xff0c;活动 https://t.aliyun.com/U/bLynLC 配置为云服务器ECS经济型e实例ecs.e-c1m2.large&#xff0c;3M固定带宽&#xff0c;系统盘为40GB ESSD Entry&#xff0c;活动打开如下图&#xff1a; 阿里云2核4G服务器优惠价格30元…

Python武器库开发-武器库篇之C段扫描器开发(四十三)

Python武器库开发-武器库篇之C段扫描器开发(四十三) 在我们进行渗透过程中的信息收集的步骤时&#xff0c;收集资产目标的C段也是非常重要的一部分。 C段是指互联网中的一类IP地址。IP地址是互联网上每台设备的唯一标识符。IP地址由一系列数字组成&#xff0c;通常以点分十进…

【AI视野·今日Sound 声学论文速览 第四十一期】Thu, 4 Jan 2024

AI视野今日CS.Sound 声学论文速览 Thu, 4 Jan 2024 Totally 8 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Multichannel blind speech source separation with a disjoint constraint source model Authors Jianyu Wang, Shanzheng Guan多通道卷积…

2023 CSIG青年科学家会议丨多模态大模型时代下的文档图像处理

近日&#xff0c;由中国图象图形学学会青年工作委员会发起的“第十九届中国图象图形学学会青年科学家会议”在广州召开。 会议面向国际学术前沿与国家战略需求&#xff0c;聚焦最新前沿技术和热点领域&#xff0c;邀请各界专家与青年学者进行总计超200场的高水平学术深度交流&…

漏洞复现--金蝶云星空反序列化远程代码执行

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

无旋转目标检测labelme的json格式转YOLO

# trans_labelme_to_yolo.pyimport cv2 import os import json import shutil import numpy as np from pathlib import Path from glob import globid2cls =

静态网页设计——千姿百色旅游网(HTML+CSS+JavaScript)(dw、sublime Text、webstorm、HBuilder X)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1oe411m7kH/?vd_source5f425e0074a7f92921f53ab87712357b 源码&#xff1a;https://space.bilibili.co…

获取每一个目录下面文件的个数并删除文件少于一定数据的目录( 源码)

import os import shutil # 假设 directory_A 是目录A的路径 directory_A /home/unitree/newHardDisk/VINT_data_train/go_stanford/# 遍历目录A中的每个子目录&#xff0c;并计算每个子目录中的文件数量 def count_files_in_subdirectories(directory):# 存储每个子目录文件数…

啊哈c语言——5.9逻辑挑战11(猜数游戏)

计算机会随机地给出0&#xff5e;99之间的一个整数&#xff0c;你能否猜出这个数呢&#xff1f;每猜一次&#xff0c;计算机都会告诉你猜的数是大了还是小了&#xff0c;直到你猜出这个数为止。 首先我们需要解决的第一个问题就是如何让计算机随机地产生一个整数&#xff0c;这…

云水苍苍走四方

云水苍苍走四方 &#xff08;一&#xff09;燕赵行 庚午年秋&#xff0c;内师大前旗理论班同学挚友四十余人&#xff0c;东行三千里&#xff0c;饱览燕赵风光。往事虽逝&#xff0c;记忆犹在&#xff0c;故追记之。 谒雪芹故居 这是一个红叶醉人&#xff0c;鸣蝉送幽的晚秋。我…

vue-springboot基于java的实验室安全考试系统

本系统为用户而设计制作实验室安全考试系统&#xff0c;旨在实现实验室安全考试智能化、现代化管理。本实验室安全考试管理自动化系统的开发和研制的最终目的是将实验室安全考试的运作模式从手工记录数据转变为网络信息查询管理&#xff0c;从而为现代管理人员的使用提供更多的…

1- forecasting at scale论文阅读

目录 1. 什么是时间序列2. 什么是时间序列预测3. 时间序列预测的范式4. 时间序列的专有名词介绍5. 时间序列评估 1. 什么是时间序列 按时间先后顺序出现的有序序列 2. 什么是时间序列预测 点预测&#xff1a;预测未来的某一个时间点&#xff0c;它的值到底是多少&#xff0c…

搭建宠物寄养小程序流程

近日&#xff0c;一地宠物寄养需求旺盛&#xff0c;元旦满房&#xff0c;春节几近饱和&#xff0c;一窝难求。随着市场需求的增长&#xff0c;对于很多宠物行业的商家&#xff0c;可以考虑开展宠物寄养服务&#xff0c;尤其是节假日的宠物寄养需求会更高。因此&#xff0c;商家…

我是内网灵活的狗之stack靶场1

首先我们还是老样子 我们先启动这个在win7上面的php工具&#xff0c;也就是启动php服务 然后我们启动kali攻击机 然后打开webshell工具 然后我们先打开MSF工具进行监听 启动msf工具进行监听 12345端口 然后在文件中打开虚拟终端&#xff0c;连接到目标机器内部 start 620.exe…