Assembly Segfault在一个简单的malloc和free实现中
TLDR强> 我用一个自定义逻辑超越了Assembly Segfault在一个简单的malloc和free实现中,assembly,memory-management,x86,malloc,Assembly,Memory Management,X86,Malloc,TLDR 我用一个自定义逻辑超越了malloc和free。然而,该程序是segfaulting。我有什么遗漏吗?最终,我能够用一个简单的malloc和free调用来测试它,但没有使用任何stdio.h特性。为什么会这样 完整版本 我使用LD_PRELOAD编写了一个32位汇编程序来覆盖malloc和free # PURPOSE: Program to replace malloc and free using LD_PRELOAD # # NOT
malloc
和free
。然而,该程序是segfault
ing。我有什么遗漏吗?最终,我能够用一个简单的malloc
和free
调用来测试它,但没有使用任何stdio.h
特性。为什么会这样
完整版本
我使用LD_PRELOAD
编写了一个32位汇编程序来覆盖malloc
和free
# PURPOSE: Program to replace malloc and free using LD_PRELOAD
#
# NOTES: The programs using these routines will ask for a certain
# size of memory. We actually use more than that size, but we
# put it at the beginning, before the pointer we hand back.
# We add a Size field and an Available/Unavailable marker. So
# the memory looks like this:
#
# #########################################################
# #Available Marker#Size of Memory#Actual Memory Locations#
# #########################################################
# ^Returned pointer points
# here
#
# The pointer we return only points to the actual locations
# requested to make it easier for the calling program. It
# also allows us to change our structure without the calling
# program having to change at all.
#
# The _alloc-lib-test.c program can test it:
# Dump the output and read it using hexdump.
.section .data
# Global variables
# This points to the beginning of the memory we are managing
heap_begin:
.long 0
# This points to one location past the memory we are managing
current_break:
.long 0
# Structure information
.equ HEADER_SIZE, 8 # Size of space for memory region header
.equ HDR_AVAIL_OFFSET, 0 # Location of the "Available" flag in the
# header
.equ HDR_SIZE_OFFSET, 4 # Location of the size field in the header
# Constants
.equ UNAVAILABLE, 0 # This is the number we will use to mark space
# that has been given out
.equ AVAILABLE, 1 # This is the number we will use to mark space
# that has been returned, and is available for
# giving
.equ SYS_BRK, 45 # break system call
.equ LINUX_SYSCALL, 0x80
.section .text
# Functions
# allocate_init starts
# PURPOSE: Call this function to initialize the functions
# (specifically, this sets heap_begin and current_break).
#
# PARAMS: None
#
# RETURN: None
.globl allocate_init
.type allocate_init,@function
allocate_init:
pushl %ebp # Standard function stuff
movl %esp, %ebp
# If the brk system call is called with 0 in %ebx, it returns the last
# valid usable address
movl $SYS_BRK, %eax # Find out where the break is
movl $0, %ebx
int $LINUX_SYSCALL
incl %eax # %eax now has the last valid address, and
# we want the memory location after that
movl %eax, current_break # Store the current break
movl %eax, heap_begin # Store the current break as our first
# address. This will cause the allocate
# function to get more memory from Linux
# the first time it is run
movl %ebp, %esp # Exit the function
popl %ebp
ret
# allocate_init ends
# allocate starts
# PURPOSE: This function is used to grab a section of memory. It
# checks to see if there are any free blocks, and, if not,
# it asks Linux for a new one.
#
# PARAMS: This function has one parameter - the size of the memory
# block we want to allocate
#
# RETURN: This function returns the address of the allocated memory
# in %eax. If there is no memory available, it will return 0
# in %eax
#
# %ecx - size of the requested memory (first/only parameter)
# %eax - current memory region being examined
# %ebx - current break position
# %edx - size of current memory region
#
# We scan through each memory region starting with heap_begin. We look
# at the size of each one, and if it has been allocated. If it's big
# enough for the requested size, and its available, it grabs that one.
# If it does not find a region large enough, it asks Linux for more
# memory. In that case, it moves current_break up
.globl malloc
.type malloc,@function
# Stack position of the memory size to allocate
.equ ST_MEM_SIZE, 8
malloc:
pushl %ebp # Standard function stuff
movl %esp, %ebp
movl $0, %edi
movl heap_begin, %eax # If heap_begin is 0, call allocate_init
cmpl %eax, %edi
je heap_empty
jne heap_nonempty
heap_empty:
call allocate_init
heap_nonempty:
movl ST_MEM_SIZE(%ebp), %ecx # %ecx will hold the size we are looking
# for (which is the first and only
# parameter)
movl heap_begin, %eax # %eax will hold the current
# search location
movl current_break, %ebx # %ebx will hold the current break
alloc_loop_begin: # Here we iterate through each memory
# region
cmpl %ebx, %eax # Need more memory if these are equal
je move_to_break
# Grab the size of this memory
movl HDR_SIZE_OFFSET(%eax), %edx
# If the space is unavailable, go to the next one
cmpl $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)
je next_location
cmpl %edx, %ecx # If the space is available, compare the
jle allocate_here # size to the needed size. If its big
# enough, go to allocate_here
next_location:
addl $HEADER_SIZE, %eax # The total size of the memory region
addl %edx, %eax # is the sum of the size that was
# requested when this block was created
# (currently stored in %edx), plus
# another 8 bytes for the header (4 for
# the Available/Unavailable flag, and 4
# for the Size of the region). So,
# adding %edx and $8 to %eax will get
# the address of the next memory region
jmp alloc_loop_begin # Go look at the next location
allocate_here: # If we've made it here, that means that
# the region header of the region to
# allocate is in %eax
movl $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax) # Mark space as unavailable
addl $HEADER_SIZE, %eax # Move %eax past the header to the
# usable memory (since that's what we
# return)
movl %ebp, %esp # Return from the function
popl %ebp
ret
# If we've made it here, that means that we have exhausted all addressable
# memory, and we need to ask for more. %ebx holds the current endpoint of the
# data, and %ecx holds its size
move_to_break: # We need to increase %ebx to where we _want_
# memory to end, so we add space for the
addl $HEADER_SIZE, %ebx # headers structure and add space to the
# break for the data requested
addl %ecx, %ebx
# Now its time to ask Linux for more memory
pushl %eax # Save needed registers
pushl %ecx
pushl %ebx
movl $SYS_BRK, %eax # Reset the break (%ebx has the requested
# break point)
int $LINUX_SYSCALL
# Under normal conditions, this should return the new break in %eax, which will
# be either 0 if it fails, or it will be equal to or larger than we asked for.
# We don't care in this program where it actually sets the break, so as long as
# %eax isn't 0, we don't care what it is
cmpl $0, %eax # Check for error conditions
je error
popl %ebx # Restore saved registers
popl %ecx
popl %eax
# Set this memory as unavailable, since we're about to give it away
movl $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)
# Set the size of the memory
movl %ecx, HDR_SIZE_OFFSET(%eax)
# Move %eax to the actual start of usable memory.
# %eax now holds the return value
addl $HEADER_SIZE, %eax
movl %ebx, current_break # Save the new break
movl %ebp, %esp # Return the function
popl %ebp
ret
error:
movl $0, %eax # On error, we return zero
movl %ebp, %esp
popl %ebp
ret
# allocate ends
# deallocate starts
# PURPOSE: The purpose of this function is to give back a region of
# memory to the pool after we're done using it.
#
# PARAMS: The only parameter is the address of the memory we want to
# return to the memory pool.
#
# RETURN: There is no return value
#
# If you remember, we actually hand the program the start of the memory
# that they can use, which is 8 storage locations after the actual
# start of the memory region. All we have to do is go back 8 locations
# and mark that memory as available, so that the allocate function
# knows it can use it.
.globl free
.type free,@function
# Stack position of the memory region to free
.equ ST_MEMORY_SEG, 4
free:
# Since the function is so simple, we don't need any of the fancy
# function stuff
# Get the address of the memory to free (normally this is 8(%ebp), but
# since we didn't push %ebp or move %esp to %ebp, we can just do
# 4(%esp))
movl ST_MEMORY_SEG(%esp), %eax
# Get the pointer to the real beginning of the memory
subl $HEADER_SIZE, %eax
# Mark it as available
movl $AVAILABLE, HDR_AVAIL_OFFSET(%eax)
ret
# deallocate ends
这是在64位系统中交叉编译的,如下所示:
as --32 alloc-lib.asm -o alloc-lib.o
ld --shared "-melf_i386" alloc-lib.o -o alloc-lib.so
但是,当我在32位系统上使用以下方法测试它时,它只会产生segfault
:
LD_PRELOAD=./alloc-lib.so ls
我决定编写一个简单的C程序来分配一个整数1来测试它,但甚至不能进行printf
函数调用。它segfault
s
最后,我修改了C程序,使用putchar
将字节从新内存位置转储到文件中,并使用hextump
验证值<代码>putcharAPI
在没有SEGFULT的情况下工作
#include <stdlib.h>
#include <stdio.h>
int main(){
int *ptra;
char *cptra;
ptra = malloc(4);
*ptra = 1;
cptra = ptra;
putchar(*cptra);
putchar(*(cptra + 1));
putchar(*(cptra + 2));
putchar(*(cptra + 3));
free(ptra);
}
#包括
#包括
int main(){
int*ptra;
char*cptra;
ptra=malloc(4);
*ptra=1;
cptra=ptra;
putchar(*cptra);
putchar(*(cptra+1));
putchar(*(cptra+2));
putchar(*(cptra+3));
免费(ptra);
}
ls
和printf
函数调用?假设这可能是因为我没有实现realloc
等,那么该程序不会只使用标准的realloc
malloc
,free
分配一个整数,将其转储到一个文件中,并使用hextump
验证它这是本书中的练习。您不仅需要覆盖
malloc
和free
,还需要覆盖所有其他内存分配函数。请进一步注意,stdio在内部使用这些函数进行缓冲区管理,因此除非完全忽略stdio,否则无法解决此问题。告诉我你在为什么操作系统和libc编程,我可以试着找出这些是什么函数。好的,谢谢你,@fuz。我正在使用Arch-Linux
运行一个32位chroot
环境,使用ldd(GNU-libc)2.12
。只要你说我需要这些额外的函数,那么我认为现在这应该是一个足够的答案(你不需要调试并告诉我函数名,以后会保存)。在这种情况下,替代测试用例可以吗?知道程序没有问题真的很有帮助。我在任何地方都看不到替代测试用例。您可以做的一件简单的事情是将对putchar
的调用替换为对write
的调用(有关如何调用write
,请参阅手册)。由于write
是一个系统调用,它根本不涉及任何内存分配,您不应该遇到问题。是的,但是printf
调用malloc
和其他内存管理函数,如realloc
,您不包装它们,从而导致观察到的问题。是的,它会realloc
访问malloc
的内部数据结构。如果您传递一个指向未由库的malloc
分配的内存的指针,则行为未定义,您的程序可能会崩溃。为什么您希望libc的realloc
能够处理您从自己的代码中分配的块?