git bisec 使用实例

本文记录了一次使用git bisect工具定位Linux内核中RDS模块内存泄漏问题的过程,并详细展示了如何通过逐步测试缩小问题范围直至找到引入bug的首次提交。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect start
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad dbe686b56faa362865b54f2e1dfa3ecc88977dd8
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect good 67edd8ef996168b7dbbdc00d1ce483b2b76cea5a
Bisecting: 108 revisions left to test after this (roughly 7 steps)
[0e3446e8bb3ae078d66434c1cb236b4d1546bfe8] mm: mmu_notifier: fix freed page still mapped in secondary MMU
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 54 revisions left to test after this (roughly 6 steps)
[3f59f82650136ab39b7998439750199a747781e5] bridge: set priority of STP packets
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 26 revisions left to test after this (roughly 5 steps)
[937eabb356279bd9d74de453f0052ba92f5733c5] rose: fix info leak via msg_name in rose_recvmsg()
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect good
Bisecting: 13 revisions left to test after this (roughly 4 steps)
[f85a52045dda90826bbb59b6b68f6332529eadab] net: sctp: sctp_endpoint_free: zero out secret key data
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 6 revisions left to test after this (roughly 3 steps)
[c327aec64e7ddd03b70e357331de5350ed2fe220] ax25: fix info leak via msg_name in ax25_recvmsg()
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[89b258e930ecf55c2c9a7c0fb0c428dc45f87980] llc: Fix missing msg_namelen update in llc_ui_recvmsg()
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[d798b105cee33543b235410d2510219fa887b4d2] llc: fix info leak via getsockname()
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22] rds: set correct msg_namelen
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect bad
dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22 is the first bad commit
commit dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22
Author: Weiping Pan <wpan@redhat.com>
Date:   Mon Jul 23 10:37:48 2012 +0800

    rds: set correct msg_namelen
   
    commit 06b6a1cf6e776426766298d055bb3991957d90a7 upstream.
   
    Jay Fenlason (fenlason@redhat.com) found a bug,
    that recvfrom() on an RDS socket can return the contents of random kernel
    memory to userspace if it was called with a address length larger than
    sizeof(struct sockaddr_in).
    rds_recvmsg() also fails to set the addr_len paramater properly before
    returning, but that's just a bug.
    There are also a number of cases wher recvfrom() can return an entirely bogus
    address. Anything in rds_recvmsg() that returns a non-negative value but does
    not go through the "sin = (struct sockaddr_in *)msg->msg_name;" code path
    at the end of the while(1) loop will return up to 128 bytes of kernel memory
    to userspace.
   
    And I write two test programs to reproduce this bug, you will see that in
    rds_server, fromAddr will be overwritten and the following sock_fd will be
    destroyed.
    Yes, it is the programmer's fault to set msg_namelen incorrectly, but it is
    better to make the kernel copy the real length of address to user space in
    such case.
   
    How to run the test programs ?
    I test them on 32bit x86 system, 3.5.0-rc7.
   
    1 compile
    gcc -o rds_client rds_client.c
    gcc -o rds_server rds_server.c
   
    2 run ./rds_server on one console
   
    3 run ./rds_client on another console
   
    4 you will see something like:
    server is waiting to receive data...
    old socket fd=3
    server received data from client:data from client
    msg.msg_namelen=32
    new socket fd=-1067277685
    sendmsg()
    : Bad file descriptor
   
    /***************** rds_client.c ********************/
   
    int main(void)
    {
        int sock_fd;
        struct sockaddr_in serverAddr;
        struct sockaddr_in toAddr;
        char recvBuffer[128] = "data from client";
        struct msghdr msg;
        struct iovec iov;
   
        sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
        if (sock_fd < 0) {
                perror("create socket error\n");
                exit(1);
        }
   
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        serverAddr.sin_port = htons(4001);
   
        if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
                perror("bind() error\n");
                close(sock_fd);
                exit(1);
        }
   
        memset(&toAddr, 0, sizeof(toAddr));
        toAddr.sin_family = AF_INET;
        toAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        toAddr.sin_port = htons(4000);
        msg.msg_name = &toAddr;
        msg.msg_namelen = sizeof(toAddr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = strlen(recvBuffer) + 1;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
   
        if (sendmsg(sock_fd, &msg, 0) == -1) {
                perror("sendto() error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("client send data:%s\n", recvBuffer);
   
        memset(recvBuffer, '\0', 128);
   
        msg.msg_name = &toAddr;
        msg.msg_namelen = sizeof(toAddr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = 128;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
        if (recvmsg(sock_fd, &msg, 0) == -1) {
                perror("recvmsg() error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("receive data from server:%s\n", recvBuffer);
   
        close(sock_fd);
   
        return 0;
    }
   
    /***************** rds_server.c ********************/
   
    int main(void)
    {
        struct sockaddr_in fromAddr;
        int sock_fd;
        struct sockaddr_in serverAddr;
        unsigned int addrLen;
        char recvBuffer[128];
        struct msghdr msg;
        struct iovec iov;
   
        sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
        if(sock_fd < 0) {
                perror("create socket error\n");
                exit(0);
        }
   
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        serverAddr.sin_port = htons(4000);
        if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
                perror("bind error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("server is waiting to receive data...\n");
        msg.msg_name = &fromAddr;
   
        /*
         * I add 16 to sizeof(fromAddr), ie 32,
         * and pay attention to the definition of fromAddr,
         * recvmsg() will overwrite sock_fd,
         * since kernel will copy 32 bytes to userspace.
         *
         * If you just use sizeof(fromAddr), it works fine.
         * */
        msg.msg_namelen = sizeof(fromAddr) + 16;
        /* msg.msg_namelen = sizeof(fromAddr); */
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = 128;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
   
        while (1) {
                printf("old socket fd=%d\n", sock_fd);
                if (recvmsg(sock_fd, &msg, 0) == -1) {
                        perror("recvmsg() error\n");
                        close(sock_fd);
                        exit(1);
                }
                printf("server received data from client:%s\n", recvBuffer);
                printf("msg.msg_namelen=%d\n", msg.msg_namelen);
                printf("new socket fd=%d\n", sock_fd);
                strcat(recvBuffer, "--data from server");
                if (sendmsg(sock_fd, &msg, 0) == -1) {
                        perror("sendmsg()\n");
                        close(sock_fd);
                        exit(1);
                }
        }
   
        close(sock_fd);
        return 0;
    }
   
    Signed-off-by: Weiping Pan <wpan@redhat.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>

:040000 040000 7f2951d180ba4a5df3f9b359dfcfa09646038612 879afe6065053c118560f7e90cc22b8ef33357e2 M      net
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect log
git bisect start
# bad: [dbe686b56faa362865b54f2e1dfa3ecc88977dd8] video:uvesafb: Fix oops that uvesafb try to execute NX-protected page
git bisect bad dbe686b56faa362865b54f2e1dfa3ecc88977dd8
# good: [67edd8ef996168b7dbbdc00d1ce483b2b76cea5a] net: Fix ip link add netns oops
git bisect good 67edd8ef996168b7dbbdc00d1ce483b2b76cea5a
# bad: [0e3446e8bb3ae078d66434c1cb236b4d1546bfe8] mm: mmu_notifier: fix freed page still mapped in secondary MMU
git bisect bad 0e3446e8bb3ae078d66434c1cb236b4d1546bfe8
# bad: [3f59f82650136ab39b7998439750199a747781e5] bridge: set priority of STP packets
git bisect bad 3f59f82650136ab39b7998439750199a747781e5
# good: [937eabb356279bd9d74de453f0052ba92f5733c5] rose: fix info leak via msg_name in rose_recvmsg()
git bisect good 937eabb356279bd9d74de453f0052ba92f5733c5
# bad: [f85a52045dda90826bbb59b6b68f6332529eadab] net: sctp: sctp_endpoint_free: zero out secret key data
git bisect bad f85a52045dda90826bbb59b6b68f6332529eadab
# bad: [c327aec64e7ddd03b70e357331de5350ed2fe220] ax25: fix info leak via msg_name in ax25_recvmsg()
git bisect bad c327aec64e7ddd03b70e357331de5350ed2fe220
# bad: [89b258e930ecf55c2c9a7c0fb0c428dc45f87980] llc: Fix missing msg_namelen update in llc_ui_recvmsg()
git bisect bad 89b258e930ecf55c2c9a7c0fb0c428dc45f87980
# bad: [d798b105cee33543b235410d2510219fa887b4d2] llc: fix info leak via getsockname()
git bisect bad d798b105cee33543b235410d2510219fa887b4d2
# bad: [dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22] rds: set correct msg_namelen
git bisect bad dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22
# first bad commit: [dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22] rds: set correct msg_namelen
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect visual
usage: git bisect [help|start|bad|good|skip|next|reset|visualize|replay|log|run]
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect visualize
commit dbaaf2eb9d0955c2ed5aa9cdd4d8607204ceec22
Author: Weiping Pan <wpan@redhat.com>
Date:   Mon Jul 23 10:37:48 2012 +0800

    rds: set correct msg_namelen
   
    commit 06b6a1cf6e776426766298d055bb3991957d90a7 upstream.
   
    Jay Fenlason (fenlason@redhat.com) found a bug,
    that recvfrom() on an RDS socket can return the contents of random kernel
    memory to userspace if it was called with a address length larger than
    sizeof(struct sockaddr_in).
    rds_recvmsg() also fails to set the addr_len paramater properly before
    returning, but that's just a bug.
    There are also a number of cases wher recvfrom() can return an entirely bogus
    address. Anything in rds_recvmsg() that returns a non-negative value but does
    not go through the "sin = (struct sockaddr_in *)msg->msg_name;" code path
    at the end of the while(1) loop will return up to 128 bytes of kernel memory
    to userspace.
   
    And I write two test programs to reproduce this bug, you will see that in
    rds_server, fromAddr will be overwritten and the following sock_fd will be
    destroyed.
    Yes, it is the programmer's fault to set msg_namelen incorrectly, but it is
    better to make the kernel copy the real length of address to user space in
    such case.
   
    How to run the test programs ?
    I test them on 32bit x86 system, 3.5.0-rc7.
   
    1 compile
    gcc -o rds_client rds_client.c
    gcc -o rds_server rds_server.c
   
    2 run ./rds_server on one console
   
    3 run ./rds_client on another console
   
    4 you will see something like:
    server is waiting to receive data...
    old socket fd=3
    server received data from client:data from client
    msg.msg_namelen=32
    new socket fd=-1067277685
    sendmsg()
    : Bad file descriptor
   
    /***************** rds_client.c ********************/
   
    int main(void)
    {
        int sock_fd;
        struct sockaddr_in serverAddr;
        struct sockaddr_in toAddr;
        char recvBuffer[128] = "data from client";
        struct msghdr msg;
        struct iovec iov;
   
        sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
        if (sock_fd < 0) {
                perror("create socket error\n");
                exit(1);
        }
   
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        serverAddr.sin_port = htons(4001);
   
        if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
                perror("bind() error\n");
                close(sock_fd);
                exit(1);
        }
   
        memset(&toAddr, 0, sizeof(toAddr));
        toAddr.sin_family = AF_INET;
        toAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        toAddr.sin_port = htons(4000);
        msg.msg_name = &toAddr;
        msg.msg_namelen = sizeof(toAddr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = strlen(recvBuffer) + 1;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
   
        if (sendmsg(sock_fd, &msg, 0) == -1) {
                perror("sendto() error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("client send data:%s\n", recvBuffer);
   
        memset(recvBuffer, '\0', 128);
   
        msg.msg_name = &toAddr;
        msg.msg_namelen = sizeof(toAddr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = 128;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
        if (recvmsg(sock_fd, &msg, 0) == -1) {
                perror("recvmsg() error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("receive data from server:%s\n", recvBuffer);
   
        close(sock_fd);
   
        return 0;
    }
   
    /***************** rds_server.c ********************/
   
    int main(void)
    {
        struct sockaddr_in fromAddr;
        int sock_fd;
        struct sockaddr_in serverAddr;
        unsigned int addrLen;
        char recvBuffer[128];
        struct msghdr msg;
        struct iovec iov;
   
        sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
        if(sock_fd < 0) {
                perror("create socket error\n");
                exit(0);
        }
   
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        serverAddr.sin_port = htons(4000);
        if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
                perror("bind error\n");
                close(sock_fd);
                exit(1);
        }
   
        printf("server is waiting to receive data...\n");
        msg.msg_name = &fromAddr;
   
        /*
         * I add 16 to sizeof(fromAddr), ie 32,
         * and pay attention to the definition of fromAddr,
         * recvmsg() will overwrite sock_fd,
         * since kernel will copy 32 bytes to userspace.
         *
         * If you just use sizeof(fromAddr), it works fine.
         * */
        msg.msg_namelen = sizeof(fromAddr) + 16;
        /* msg.msg_namelen = sizeof(fromAddr); */
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iov->iov_base = recvBuffer;
        msg.msg_iov->iov_len = 128;
        msg.msg_control = 0;
        msg.msg_controllen = 0;
        msg.msg_flags = 0;
   
        while (1) {
                printf("old socket fd=%d\n", sock_fd);
                if (recvmsg(sock_fd, &msg, 0) == -1) {
                        perror("recvmsg() error\n");
                        close(sock_fd);
                        exit(1);
                }
                printf("server received data from client:%s\n", recvBuffer);
                printf("msg.msg_namelen=%d\n", msg.msg_namelen);
                printf("new socket fd=%d\n", sock_fd);
                strcat(recvBuffer, "--data from server");
                if (sendmsg(sock_fd, &msg, 0) == -1) {
                        perror("sendmsg()\n");
                        close(sock_fd);
                        exit(1);
                }
        }
   
        close(sock_fd);
        return 0;
    }
   
    Signed-off-by: Weiping Pan <wpan@redhat.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
root@OptiPlex-780:/opt/linux-git/linux-stable# git bisect reset
Previous HEAD position was dbaaf2e... rds: set correct msg_namelen
HEAD is now at 5878e06... Linux 2.6.34.15
root@OptiPlex-780:/opt/linux-git/linux-stable#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mounter625

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值