arthur-zhang / morning-up-up

78 stars 6 forks source link

1109 分享:brk、mmap 系统调用,GDB 实战 MySQL 自增主键越界调试分析 #10

Open arthur-zhang opened 3 years ago

arthur-zhang commented 3 years ago
#include <stdio.h>
#include <unistd.h>

int main() {
    void *init_brk = sbrk(0);
    printf("current brk: %p\n", init_brk);
    getchar();

    brk(init_brk + 8192); // brk 增加 8k,相当于申请 8k 内存

    printf("current brk: %p\n", sbrk(0));
    getchar();

    brk(init_brk); // brk 回退到初始位置,相当于释放内存

    printf("current brk: %p\n", sbrk(0));
    getchar();
    return 0;
}
#include <stdio.h>
#include <sys/mman.h>

int main() {
    printf("before mmap\n");
    getchar();

    char *addr = mmap(NULL, (size_t)8192 * 1024,
                      PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    printf("after mmap, addr: %p\n", addr);
    getchar();

    munmap(addr, (size_t)8192 * 1024);
    printf("after munmap\n");
    getchar();

    return 0;
}

gdb 调试

CREATE TABLE `t1` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

insert into t1(name)values("foo");
Taaang commented 3 years ago

image

IKNOWLJT commented 3 years ago

11.03

brk 和 mmap 系统调用

brk 系统调用(地址指针上下移动然后分配和释放内存),单个函数就可以实现内存的分配和释放

  1. ASLR(地址分配随机化):

    sudo sysctl -w kernel.randomize_va_space=2 ,进程每次运行时,分配空间的地址都是不一样的

  2. brk,指针从当前指针向上移动,然后移动的区域就会分配的空间

    #include <stdio.h>
    #include <unistd.h>
    
    int main() {
       void *init_brk = sbrk(0);
       printf("current brk: %p\n", init_brk);
       getchar();
    
       brk(init_brk + 8192); // brk 增加 8k,相当于申请 8k 内存
    
       printf("current brk: %p\n", sbrk(0));
       getchar();
    
       brk(init_brk); // brk 回退到初始位置,相当于释放内存
    
       printf("current brk: %p\n", sbrk(0));
       getchar();
       return 0;
    }

mmap 系统调用

  1. 文件背景内存映射:将一个文件的部分或者全部映射到虚拟内存,可以通过访问内存的方式读取、修改内容

  2. 匿名内存映射:这种情况的内存映射没有对应的文件,使用malloc触发的系统调用属于mmap系统调用

    申请内存:mmap

    释放内存:munmap

    #include <stdio.h>
    #include <sys/mman.h>
    
    int main() {
       printf("before mmap\n");
       getchar();
    
       char *addr = mmap(NULL, (size_t)8192 * 1024,
                         PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
       printf("after mmap, addr: %p\n", addr);
       getchar();
    
       munmap(addr, (size_t)8192 * 1024);
       printf("after munmap\n");
       getchar();
    
       return 0;
    }

    free()和munmap()

gdb 调试mysql源码

1. mysql 源码编译安装
1. git clone https://github.com/mysql/mysql-server.git
2. cd mysql-server
3. git checkout mysql-5.7.19
4. cmake -DCMAKE_INSTALL_PREFIX=/home/parallels/sourceCode/mysql-server/ \
-DMYSQL_DATADIR=/home/parallels/sourceCode/mysql-server/data/ \
-DSYSCONFDIR=/home/parallels/sourceCode/mysql-server/ \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_FEDERATED_STORAGE_ENGINE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DMYSQL_UNIX_ADDR=/home/parallels/sourceCode/mysql-server/mysql.sock \
-DMYSQL_TCP_PORT=3306 \
-DENABLED_LOCAL_INFILE=1 \
-DEXTRA_CHARSETS=all \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DMYSQL_USER=mysql \
-DWITH_BINLOG_PREALLOC=ON \
-DWITH_BOOST=/usr/local/boost/boost_1_59_0 -DWITH_DEBUG=1

5. make
6 make install
2. 编译可能会遇到问题

cmake 的时候可能有会遇到 ncurses-devel 没有和 boost库没有

yum -y install gcc gcc-c++ ncurses ncurses-devel cmake

  1. wget http://downloads.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.gz
  2. tar -zxvf boost_1_59_0.tar.gz
  3. 解压之后可以在cmake命令中指定路径
3. 指定 my.cnf 配置文件

指定 my.cnf 文件

[mysqld]
log-error=/home/parallels/sourceCode/mysql-server/error.log2
datadir=/home/parallels/sourceCode/mysql-server/data
pid-file=/home/parallels/sourceCode/mysql-server/mysql.pid2
skip-grant-tables
innodb_file_per_table=1

port=33060
transaction_isolation = READ-COMMITTED

innodb_lock_wait_timeout=50000

innodb_purge_stop_now=1

[client]
# 客户端来源数据的默认字符集
default-character-set = utf8mb4
[mysqld]
# 服务端默认字符集
character-set-server=utf8mb4
# 连接层默认字符集
collation-server=utf8mb4_unicode_ci
[mysql]
# 数据库默认字符集
default-character-set = utf8mb4
4. 启动 (安装根目录下执行) ---- 注意不要用root身份执行

./bin/mysqld --defaults-file=/home/parallels/sourceCode/mysql-server/my.cnf --debug

5. gdb attach (打上断点调试吧) --- 唯一的问题,就是不知道调试那个文件,哪一行?只能找到网上说的一个insert的必走的方法,bt看堆栈一步一步next,不过还是没有找的很清楚

image-20201109210217024

LinForTracy commented 3 years ago

image

wefun94 commented 3 years ago

malloc/free函数分配释放内存,这两个函数底层是由brk,mmap这些系统调用函数实现的。 (1)brk是将数据段(.data)的最高地址指针_edata往高地址推; (2)mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。 mallo小于128k的内存时,调用brk进行分配内存,当大于128k时,使用mmap分配内存。这个操作不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系。