diff options
| author | Sakib Sajal <sakib.sajal@windriver.com> | 2022-08-19 17:59:06 -0400 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-08-28 07:51:29 +0100 |
| commit | c78d028649c0684e61977d4d78c39195cb370990 (patch) | |
| tree | 1ac1c9bfa1b1e8f7ff37ccf945c32c2b7b0608dc | |
| parent | ee16bcef39ed7f97edbf53c97a7b087e0904af0b (diff) | |
| download | poky-c78d028649c0684e61977d4d78c39195cb370990.tar.gz | |
u-boot: fix CVE-2022-30552
Backport patch to fix CVE-2022-30552.
(From OE-Core rev: db5212cbe7537036108682f0f3a9316ca3c06fc1)
Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-bsp/u-boot/files/0001-net-Check-for-the-minimum-IP-fragmented-datagram-siz.patch | 207 | ||||
| -rw-r--r-- | meta/recipes-bsp/u-boot/u-boot_2022.01.bb | 1 |
2 files changed, 208 insertions, 0 deletions
diff --git a/meta/recipes-bsp/u-boot/files/0001-net-Check-for-the-minimum-IP-fragmented-datagram-siz.patch b/meta/recipes-bsp/u-boot/files/0001-net-Check-for-the-minimum-IP-fragmented-datagram-siz.patch new file mode 100644 index 0000000000..3f9cc7776b --- /dev/null +++ b/meta/recipes-bsp/u-boot/files/0001-net-Check-for-the-minimum-IP-fragmented-datagram-siz.patch | |||
| @@ -0,0 +1,207 @@ | |||
| 1 | From c7cab39de5e4b22620248a190b3d2ee46cff38c2 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Fabio Estevam <festevam@denx.de> | ||
| 3 | Date: Thu, 26 May 2022 11:14:37 -0300 | ||
| 4 | Subject: [PATCH] net: Check for the minimum IP fragmented datagram size | ||
| 5 | |||
| 6 | Nicolas Bidron and Nicolas Guigo reported the two bugs below: | ||
| 7 | |||
| 8 | " | ||
| 9 | ----------BUG 1---------- | ||
| 10 | |||
| 11 | In compiled versions of U-Boot that define CONFIG_IP_DEFRAG, a value of | ||
| 12 | `ip->ip_len` (IP packet header's Total Length) higher than `IP_HDR_SIZE` | ||
| 13 | and strictly lower than `IP_HDR_SIZE+8` will lead to a value for `len` | ||
| 14 | comprised between `0` and `7`. This will ultimately result in a | ||
| 15 | truncated division by `8` resulting value of `0` forcing the hole | ||
| 16 | metadata and fragment to point to the same location. The subsequent | ||
| 17 | memcopy will overwrite the hole metadata with the fragment data. Through | ||
| 18 | a second fragment, this can be exploited to write to an arbitrary offset | ||
| 19 | controlled by that overwritten hole metadata value. | ||
| 20 | |||
| 21 | This bug is only exploitable locally as it requires crafting two packets | ||
| 22 | the first of which would most likely be dropped through routing due to | ||
| 23 | its unexpectedly low Total Length. However, this bug can potentially be | ||
| 24 | exploited to root linux based embedded devices locally. | ||
| 25 | |||
| 26 | ```C | ||
| 27 | static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) | ||
| 28 | { | ||
| 29 | static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN); | ||
| 30 | static u16 first_hole, total_len; | ||
| 31 | struct hole *payload, *thisfrag, *h, *newh; | ||
| 32 | struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff; | ||
| 33 | uchar *indata = (uchar *)ip; | ||
| 34 | int offset8, start, len, done = 0; | ||
| 35 | u16 ip_off = ntohs(ip->ip_off); | ||
| 36 | |||
| 37 | /* payload starts after IP header, this fragment is in there */ | ||
| 38 | payload = (struct hole *)(pkt_buff + IP_HDR_SIZE); | ||
| 39 | offset8 = (ip_off & IP_OFFS); | ||
| 40 | thisfrag = payload + offset8; | ||
| 41 | start = offset8 * 8; | ||
| 42 | len = ntohs(ip->ip_len) - IP_HDR_SIZE; | ||
| 43 | ``` | ||
| 44 | |||
| 45 | The last line of the previous excerpt from `u-boot/net/net.c` shows how | ||
| 46 | the attacker can control the value of `len` to be strictly lower than | ||
| 47 | `8` by issuing a packet with `ip_len` between `21` and `27` | ||
| 48 | (`IP_HDR_SIZE` has a value of `20`). | ||
| 49 | |||
| 50 | Also note that `offset8` here is `0` which leads to `thisfrag = payload`. | ||
| 51 | |||
| 52 | ```C | ||
| 53 | } else if (h >= thisfrag) { | ||
| 54 | /* overlaps with initial part of the hole: move this hole */ | ||
| 55 | newh = thisfrag + (len / 8); | ||
| 56 | *newh = *h; | ||
| 57 | h = newh; | ||
| 58 | if (h->next_hole) | ||
| 59 | payload[h->next_hole].prev_hole = (h - payload); | ||
| 60 | if (h->prev_hole) | ||
| 61 | payload[h->prev_hole].next_hole = (h - payload); | ||
| 62 | else | ||
| 63 | first_hole = (h - payload); | ||
| 64 | |||
| 65 | } else { | ||
| 66 | ``` | ||
| 67 | |||
| 68 | Lower down the same function, execution reaches the above code path. | ||
| 69 | Here, `len / 8` evaluates to `0` leading to `newh = thisfrag`. Also note | ||
| 70 | that `first_hole` here is `0` since `h` and `payload` point to the same | ||
| 71 | location. | ||
| 72 | |||
| 73 | ```C | ||
| 74 | /* finally copy this fragment and possibly return whole packet */ | ||
| 75 | memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len); | ||
| 76 | ``` | ||
| 77 | |||
| 78 | Finally, in the above excerpt the `memcpy` overwrites the hole metadata | ||
| 79 | since `thisfrag` and `h` both point to the same location. The hole | ||
| 80 | metadata is effectively overwritten with arbitrary data from the | ||
| 81 | fragmented IP packet data. If `len` was crafted to be `6`, `last_byte`, | ||
| 82 | `next_hole`, and `prev_hole` of the `first_hole` can be controlled by | ||
| 83 | the attacker. | ||
| 84 | |||
| 85 | Finally the arbitrary offset write occurs through a second fragment that | ||
| 86 | only needs to be crafted to write data in the hole pointed to by the | ||
| 87 | previously controlled hole metadata (`next_hole`) from the first packet. | ||
| 88 | |||
| 89 | ### Recommendation | ||
| 90 | |||
| 91 | Handle cases where `len` is strictly lower than 8 by preventing the | ||
| 92 | overwrite of the hole metadata during the memcpy of the fragment. This | ||
| 93 | could be achieved by either: | ||
| 94 | * Moving the location where the hole metadata is stored when `len` is | ||
| 95 | lower than `8`. | ||
| 96 | * Or outright rejecting fragmented IP datagram with a Total Length | ||
| 97 | (`ip_len`) lower than 28 bytes which is the minimum valid fragmented IP | ||
| 98 | datagram size (as defined as the minimum fragment of 8 octets in the IP | ||
| 99 | Specification Document: | ||
| 100 | [RFC791](https://datatracker.ietf.org/doc/html/rfc791) page 25). | ||
| 101 | |||
| 102 | ----------BUG 2---------- | ||
| 103 | |||
| 104 | In compiled versions of U-Boot that define CONFIG_IP_DEFRAG, a value of | ||
| 105 | `ip->ip_len` (IP packet header's Total Length) lower than `IP_HDR_SIZE` | ||
| 106 | will lead to a negative value for `len` which will ultimately result in | ||
| 107 | a buffer overflow during the subsequent `memcpy` that uses `len` as it's | ||
| 108 | `count` parameter. | ||
| 109 | |||
| 110 | This bug is only exploitable on local ethernet as it requires crafting | ||
| 111 | an invalid packet to include an unexpected `ip_len` value in the IP UDP | ||
| 112 | header that's lower than the minimum accepted Total Length of a packet | ||
| 113 | (21 as defined in the IP Specification Document: | ||
| 114 | [RFC791](https://datatracker.ietf.org/doc/html/rfc791)). Such packet | ||
| 115 | would in all likelihood be dropped while being routed to its final | ||
| 116 | destination through most routing equipment and as such requires the | ||
| 117 | attacker to be in a local position in order to be exploited. | ||
| 118 | |||
| 119 | ```C | ||
| 120 | static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) | ||
| 121 | { | ||
| 122 | static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN); | ||
| 123 | static u16 first_hole, total_len; | ||
| 124 | struct hole *payload, *thisfrag, *h, *newh; | ||
| 125 | struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff; | ||
| 126 | uchar *indata = (uchar *)ip; | ||
| 127 | int offset8, start, len, done = 0; | ||
| 128 | u16 ip_off = ntohs(ip->ip_off); | ||
| 129 | |||
| 130 | /* payload starts after IP header, this fragment is in there */ | ||
| 131 | payload = (struct hole *)(pkt_buff + IP_HDR_SIZE); | ||
| 132 | offset8 = (ip_off & IP_OFFS); | ||
| 133 | thisfrag = payload + offset8; | ||
| 134 | start = offset8 * 8; | ||
| 135 | len = ntohs(ip->ip_len) - IP_HDR_SIZE; | ||
| 136 | ``` | ||
| 137 | |||
| 138 | The last line of the previous excerpt from `u-boot/net/net.c` shows | ||
| 139 | where the underflow to a negative `len` value occurs if `ip_len` is set | ||
| 140 | to a value strictly lower than 20 (`IP_HDR_SIZE` being 20). Also note | ||
| 141 | that in the above excerpt the `pkt_buff` buffer has a size of | ||
| 142 | `CONFIG_NET_MAXDEFRAG` which defaults to 16 KB but can range from 1KB to | ||
| 143 | 64 KB depending on configurations. | ||
| 144 | |||
| 145 | ```C | ||
| 146 | /* finally copy this fragment and possibly return whole packet */ | ||
| 147 | memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len); | ||
| 148 | ``` | ||
| 149 | |||
| 150 | In the above excerpt the `memcpy` overflows the destination by | ||
| 151 | attempting to make a copy of nearly 4 gigabytes in a buffer that's | ||
| 152 | designed to hold `CONFIG_NET_MAXDEFRAG` bytes at most which leads to a DoS. | ||
| 153 | |||
| 154 | ### Recommendation | ||
| 155 | |||
| 156 | Stop processing of the packet if `ip_len` is lower than 21 (as defined | ||
| 157 | by the minimum length of a data carrying datagram in the IP | ||
| 158 | Specification Document: | ||
| 159 | [RFC791](https://datatracker.ietf.org/doc/html/rfc791) page 34)." | ||
| 160 | |||
| 161 | Add a check for ip_len lesser than 28 and stop processing the packet | ||
| 162 | in this case. | ||
| 163 | |||
| 164 | Such a check covers the two reported bugs. | ||
| 165 | |||
| 166 | Reported-by: Nicolas Bidron <nicolas.bidron@nccgroup.com> | ||
| 167 | Signed-off-by: Fabio Estevam <festevam@denx.de> | ||
| 168 | |||
| 169 | Upstream-Status: Backport [b85d130ea0cac152c21ec38ac9417b31d41b5552] | ||
| 170 | CVE: CVE-2022-30552 | ||
| 171 | |||
| 172 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
| 173 | --- | ||
| 174 | include/net.h | 2 ++ | ||
| 175 | net/net.c | 3 +++ | ||
| 176 | 2 files changed, 5 insertions(+) | ||
| 177 | |||
| 178 | diff --git a/include/net.h b/include/net.h | ||
| 179 | index cec8c98618..09d7e9b9e8 100644 | ||
| 180 | --- a/include/net.h | ||
| 181 | +++ b/include/net.h | ||
| 182 | @@ -397,6 +397,8 @@ struct ip_hdr { | ||
| 183 | |||
| 184 | #define IP_HDR_SIZE (sizeof(struct ip_hdr)) | ||
| 185 | |||
| 186 | +#define IP_MIN_FRAG_DATAGRAM_SIZE (IP_HDR_SIZE + 8) | ||
| 187 | + | ||
| 188 | /* | ||
| 189 | * Internet Protocol (IP) + UDP header. | ||
| 190 | */ | ||
| 191 | diff --git a/net/net.c b/net/net.c | ||
| 192 | index c2992a0908..f5400e6dbc 100644 | ||
| 193 | --- a/net/net.c | ||
| 194 | +++ b/net/net.c | ||
| 195 | @@ -907,6 +907,9 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) | ||
| 196 | int offset8, start, len, done = 0; | ||
| 197 | u16 ip_off = ntohs(ip->ip_off); | ||
| 198 | |||
| 199 | + if (ip->ip_len < IP_MIN_FRAG_DATAGRAM_SIZE) | ||
| 200 | + return NULL; | ||
| 201 | + | ||
| 202 | /* payload starts after IP header, this fragment is in there */ | ||
| 203 | payload = (struct hole *)(pkt_buff + IP_HDR_SIZE); | ||
| 204 | offset8 = (ip_off & IP_OFFS); | ||
| 205 | -- | ||
| 206 | 2.33.0 | ||
| 207 | |||
diff --git a/meta/recipes-bsp/u-boot/u-boot_2022.01.bb b/meta/recipes-bsp/u-boot/u-boot_2022.01.bb index a6a15d698f..04f60adaa5 100644 --- a/meta/recipes-bsp/u-boot/u-boot_2022.01.bb +++ b/meta/recipes-bsp/u-boot/u-boot_2022.01.bb | |||
| @@ -5,6 +5,7 @@ SRC_URI:append = " file://0001-riscv32-Use-double-float-ABI-for-rv32.patch \ | |||
| 5 | file://0001-riscv-fix-build-with-binutils-2.38.patch \ | 5 | file://0001-riscv-fix-build-with-binutils-2.38.patch \ |
| 6 | file://0001-i2c-fix-stack-buffer-overflow-vulnerability-in-i2c-m.patch \ | 6 | file://0001-i2c-fix-stack-buffer-overflow-vulnerability-in-i2c-m.patch \ |
| 7 | file://0001-fs-squashfs-sqfs_read-Prevent-arbitrary-code-executi.patch \ | 7 | file://0001-fs-squashfs-sqfs_read-Prevent-arbitrary-code-executi.patch \ |
| 8 | file://0001-net-Check-for-the-minimum-IP-fragmented-datagram-siz.patch \ | ||
| 8 | " | 9 | " |
| 9 | 10 | ||
| 10 | DEPENDS += "bc-native dtc-native python3-setuptools-native" | 11 | DEPENDS += "bc-native dtc-native python3-setuptools-native" |
