积沙成塔--C语言

2024-01-31 02:30
文章标签 语言 积沙成塔

本文主要是介绍积沙成塔--C语言,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1. 关于有符号数与无符号数的建议

    就像我们看到的那样,有符号数到无符号数的隐式强制类型转换导致了某些非直观的行为。而这些非直观的特性经常导致程序错误,并且这种包含隐式强制类型转换细微差别的错误很难被发现。 因为这种强制类型转换是在代码中没有明确指示的情况下发生的,程序员经常忽视了它的影响。下面3个例子说明了某些由于隐式强制类型转换和无符号数据类型造成的细微错误。

1.1 sum_elements

    考虑下列代码,这段代码试图计算数组 a 中所有元素的和,其中元素的数量由参数 length 给出。

/* WARNING: This is buggy code */
float sum_elements(float a[], unsigned length) {int i;float result = 0;for (i = 0; i <= length-1; i++)result += a[i];return result;
}

    当参数 length 等于 0 时,运行这段代码应该返回 0.0。但实际上,运行时会遇到一个存储器错误。

    修改代码如下:

/* WARNING: This is right code */
float sum_elements(float a[], unsigned length) {int i;float result = 0;for (i = 0; i < length; i++)result += a[i];return result;
}

1.2 strlonger

    写一个函数用来判定一个字符串是否比另一个更长。最开始你写的函数是这样的 :

/* Determine whether string s is longer than string t */
/* WARNING: This function is buggy */
int strlonger(char *s, char *t) {return strlen(s) - strlen(t) > 0;
}

    注意在头文件 stdio.h 中数据类型 size_t 是定义成 unsigned int 的。

/* Prototype for library function strlen */
size_t strlen(const char *s);

    实际上当 s 比 t 短时,strlonger 函数的返回值也是 1,为什么会出现这种情况呢?原来 strlen 的返回值类型为 size_t,C语言中将 size_t 定义为 unsigned int,当 s 比 t 短时,strlen(s) - strlen(t) 为负数,但无符号数的运算结果隐式转换为无符号数就变成了很大的无符号数。为了让函数正确工作,代码应该修改如下:

int strlonger(char *s1, char *s2)  
{  return strlen(s1) > strlen(s2);  
}

1.3 函数getpeername的安全漏洞

2002 年,从事 FreeBSD 开源操作系统项目的程序员意识到,他们对 getpeername 函数的实现存在安全漏洞。代码的简化版本如下 :

/* 
* Illustration of code vulnerability similar to that found in 
* FreeBSD’s implementation of getpeername() 
*/ /* Declaration of library function memcpy */ 
void *memcpy(void *dest, void *src, size_t n); /* Kernel memory region holding user-accessible data */ 
#define KSIZE 1024 
char kbuf[KSIZE]; /* Copy at most maxlen bytes from kernel region to user buffer */ 
int copy_from_kernel(void *user_dest, int maxlen) { /* Byte count len is minimum of buffer size and maxlen */ int len = KSIZE < maxlen ? KSIZE : maxlen; memcpy(user_dest, kbuf, len); return len; 
}

在这段代码里,第 7 行给出的是库函数 memcpy 的原型,这个函数是要将一段指定长度为 n 的字节从存储器的一个区域复制到另一个区域。

从第 14 行开始的函数 copy_from_kernel 是要将一些操作系统内核维护的数据复制到指定的用户可以访问的存储器区域。对用户来说,大多数内核维护的数据结构应该是不可读的,因为这些数据结构可能包含其他用户和系统上运行的其他作业的敏感信息,但是显示为 kbuf 的区域是用户可以读的。参数 maxlen 给出的是分配给用户的缓冲区的长度,这个缓冲区是用参数 user_dest 指示的。然后,第 16 行的计算确保复制的字节数据不会超出源或者目标缓冲区可用的范围。不过,假设有些怀有恶意的程序员在调用 copy_from_kernel 的代码中对 maxlen 使用了负数值,那么,第 16 行的最小值计算会把这个值赋给 len,然后 len 会作为参数 n 被传递给 memcpy。

不过,请注意参数n是被声明为数据类型size_t的。这个数据类型是在库文件 stdio.h中(通过typedef)被声明的。典型地,在 32 位机器上被定义为unsigned int。 既然参数n是无符号的,那么memcpy会把它当作一个非常大的正整数,并且试图将这样多字 节的数据从内核区域复制到用户的缓冲区。虽然复制这么多字节(至少 231 个)实际上不会完成, 因为程序会遇到进程中非法地址的错误,但是程序还是能读到没有被授权的内核存储器区域。

我们可以看到,这个问题是由于数据类型的不匹配造成的 :在一个地方,长度参数是有符号数 ;而另一个地方,它又是无符号数。正如这个例子表明的那样,这样的不匹配会成为缺陷的原因,甚至会导致安全漏洞。幸运的是,还没有案例报告有程序员在 FreeBSD 上利用了这个漏洞。他们发布了一个安全建议,“FreeBSD-SA-02:38.signed-error”,建议系统管理员如何应用补丁消除这个 漏洞。要修正这个缺陷,只要将 copy_from_kernel 的参数 maxlen 声明为类型 size_t,也就是与 memcpy 的参数 n 一致。同时,我们也应该将本地变量 len 和返回值声明为 size_t



转载于:https://my.oschina.net/u/2243185/blog/419256

这篇关于积沙成塔--C语言的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/662300

相关文章

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Go语言中使用JWT进行身份验证的几种方式

《Go语言中使用JWT进行身份验证的几种方式》本文主要介绍了Go语言中使用JWT进行身份验证的几种方式,包括dgrijalva/jwt-go、golang-jwt/jwt、lestrrat-go/jw... 目录简介1. github.com/dgrijalva/jwt-go安装:使用示例:解释:2. gi

Go 语言中的 Struct Tag 的用法详解

《Go语言中的StructTag的用法详解》在Go语言中,结构体字段标签(StructTag)是一种用于给字段添加元信息(metadata)的机制,常用于序列化(如JSON、XML)、ORM映... 目录一、结构体标签的基本语法二、json:"token"的具体含义三、常见的标签格式变体四、使用示例五、使用