在本文中,我想讲述如何在基于Linux的系统中验证指针。有时候,我们中的任何人都可能偶然发现SIGSEGV[1]:)但是,如果我告诉您可以检查指针是否有效呢?验证指针的技术很少,但在我看来,这一种技术是最好的。让我向您介绍msync()[2]系统调用!这个系统调用是在Linux内核中,从1.3.21开始,在Linux 2.4.19之后,它符合POSIX标准[3]。
因此,在这个小老手的帮助下,验证指针成为可能。让我们看看这个系统调用的描述。此系统调用旨在将文件与内存映射同步,即使用mmap()[4]系统调用映射的内存。在munmap()[4]系统调用之前调用它,以便在内核分配的内存页释放之前同步文件和内存,这可能很有用。
让我们看看Linux v5.8[5]中msync()的实际代码:
UNSIGNED LONG END;struct mm_struct*mm=current->;mm;struct VM_Area_struct*vma;int unmap_error=0;int error=-EINVAL;start=untagged_addr(Start);IF(FLAGS&;~(MS_ASYNC|MS_INVALIDATE|MS_SYNC))转到输出;IF(OFFSET_IN_PAGE(START))转到OUT;IF((FLAGS&;MS_ASYNC)&;&;(FLAGS&;MS_ASYNC)&;&;(FLAGS&;MS_ASYNC)&;(FLAGS&;MS_ASYNC)&;Ms_sync))转出;错误=-ENOMEM;len=(len+~page_ask)&;page_ask;end=start+len;if(end<;start)转出;error=0;if(end==start)转出;
在这段代码中,它测试传递给函数的标志是否正常,以及起始地址是否按页面大小对齐。如果其中一个条件为假,则它将返回EINVAL[6]错误。接下来,它根据LEN计算内存块的末尾。基本上,它是从start到start+len分配给内存区的最后一页的最后一个字节之后的地址。之后,它计算结束地址并对其执行基本的健全性检查。值得一提的是,就我们的目的而言,唯一有用的标志是MS_ASYNC。此标志指定对mSync()的调用立即返回并计划更新。现在,让我们继续。
Mmap_read_lock(Mm);vma=find_vma(mm,start);for(;;){struct file*file;loff_t fstart,fend;/*仍然开始<;结束。*/ERROR=-ENOMEM;IF(!VMA)GOTO OUT_UNLOCK;IF(START<;VMA->;VM_START){START=VMA->;VM_START;IF(START&gT;=END)GOTO OUT_UNLOCK;UNMAPHED_ERROR=-ENOMEM;}//...START=VMA->;VM_END;IF((标志&;MS_SYNC)&;&;文件&;&;(vma->;VM_FLAGS&;VM_SHARED){//...}Else{if(start>;=end){error=0;goto out_unlock;}vma=vma->;vm_next;}}out_unlock:mmap_read_unlock(Mm);out:返回错误?:unmap_error;
这段代码相当复杂,但不需要讨论此代码中的所有分支。开始时,它锁定mm_struct并搜索应该包含起始地址(start<;vma->;vm_end)的第一个虚拟内存区(VMA)。在循环中,它将遍历VMA区域,直到它验证该区域从头到尾都在VMA区域内。基本上,这意味着它检查此范围是否在分配的虚拟内存区域中。
有了我们刚刚获得的所有知识,让我们编写一个函数来检查进程是否分配了内存区。基本上,在此函数中,我们需要根据页面大小对齐指针地址,并调用mSync()系统调用。下面是一个简单的实现:
Int VALID_POINTER(void*p,size_t len){//获取页面大小并计算页面掩码size_t pagesz=sysconf(_SC_PageSize);size_t pagemask=~(pagesz-1);//计算基址void*base=(void*)((Size_T)p)&;pagemask);返回msync(base,len,MS_ASYNC)==0;}。
下面是VALIDATE_POINTER()函数使用的示例。就是这样!如果您想反馈或讨论这篇文章,欢迎发送电子邮件给我:)。
#include<;sys/mman.h>;#include<;unistd.h>;#include<;stdio.h>;//检查指针int有效指针(void*p,size_t len){//获取页面大小并计算页面掩码size_t pagesz=sysconf(_SC_PageSize);size_t pagemask=~(pagesz-1);//计算基址void*base=(void*)((Size_T)p)&;Pagemask);return mSync(base,len,MS_ASYNC)==0;}int main(){//Map Memory int*p=(int*)mmap(null,sizeof(Int),prot_read|prot_write,map_private|map_匿名,-1,0);//如果((void*)p==map_ailed){put(";mmap失败";);返回1;}//在取消映射内存printf之前检查指针(";在munmap之前检查指针...。";);if(VALID_POINTER((void*)p,sizeof(P)printf(";Valid\n";);Else printf(";无效\n";);*p=0x00c0ffee;//如果(munmap((void*)p,sizeof(Int){put(";munmap失败";);返回1;}//取消映射内存printf后检查指针(";检查munmap后的指针...。";);if(VALID_POINTER((void*)p,sizeof(*p)printf(";Valid\n";);Else printf(";无效\n";);//产生SIGSEGV:)*p=0xbaaaaaaad;返回0;}