Sat Jul 24 11:36:41 2021 UTC ()
acpi: /dev/acpi: fix bounds check when reading tables


(jmcneill)
diff -r1.1 -r1.2 src/sys/dev/acpi/acpi_dev.c

cvs diff -r1.1 -r1.2 src/sys/dev/acpi/acpi_dev.c (switch to unified diff)

--- src/sys/dev/acpi/acpi_dev.c 2020/12/06 02:57:30 1.1
+++ src/sys/dev/acpi/acpi_dev.c 2021/07/24 11:36:41 1.2
@@ -1,220 +1,220 @@ @@ -1,220 +1,220 @@
1/* $NetBSD: acpi_dev.c,v 1.1 2020/12/06 02:57:30 jmcneill Exp $ */ 1/* $NetBSD: acpi_dev.c,v 1.2 2021/07/24 11:36:41 jmcneill Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> 4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
15 * 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE. 26 * SUCH DAMAGE.
27 */ 27 */
28 28
29#include <sys/cdefs.h> 29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: acpi_dev.c,v 1.1 2020/12/06 02:57:30 jmcneill Exp $"); 30__KERNEL_RCSID(0, "$NetBSD: acpi_dev.c,v 1.2 2021/07/24 11:36:41 jmcneill Exp $");
31 31
32#include <sys/param.h> 32#include <sys/param.h>
33#include <sys/conf.h> 33#include <sys/conf.h>
34#include <sys/mman.h> 34#include <sys/mman.h>
35 35
36#include <dev/acpi/acpireg.h> 36#include <dev/acpi/acpireg.h>
37#include <dev/acpi/acpivar.h> 37#include <dev/acpi/acpivar.h>
38 38
39#define _COMPONENT ACPI_BUS_COMPONENT 39#define _COMPONENT ACPI_BUS_COMPONENT
40ACPI_MODULE_NAME ("acpi_dev") 40ACPI_MODULE_NAME ("acpi_dev")
41 41
42static dev_type_read(acpi_read); 42static dev_type_read(acpi_read);
43 43
44const struct cdevsw acpi_cdevsw = { 44const struct cdevsw acpi_cdevsw = {
45 .d_open = nullopen, 45 .d_open = nullopen,
46 .d_close = nullclose, 46 .d_close = nullclose,
47 .d_read = acpi_read, 47 .d_read = acpi_read,
48 .d_write = nowrite, 48 .d_write = nowrite,
49 .d_ioctl = noioctl, 49 .d_ioctl = noioctl,
50 .d_stop = nostop, 50 .d_stop = nostop,
51 .d_tty = notty, 51 .d_tty = notty,
52 .d_poll = nopoll, 52 .d_poll = nopoll,
53 .d_mmap = nommap, 53 .d_mmap = nommap,
54 .d_kqfilter = nokqfilter, 54 .d_kqfilter = nokqfilter,
55 .d_discard = nodiscard, 55 .d_discard = nodiscard,
56 .d_flag = D_OTHER | D_MPSAFE, 56 .d_flag = D_OTHER | D_MPSAFE,
57}; 57};
58 58
59/* 59/*
60 * acpi_find_table_rsdp -- 60 * acpi_find_table_rsdp --
61 * 61 *
62 * Returns true if the RSDP table is found and overlaps the specified 62 * Returns true if the RSDP table is found and overlaps the specified
63 * physical address. The table's physical start address and length 63 * physical address. The table's physical start address and length
64 * are placed in 'paddr' and 'plen' when found. 64 * are placed in 'paddr' and 'plen' when found.
65 * 65 *
66 */ 66 */
67static bool 67static bool
68acpi_find_table_rsdp(ACPI_PHYSICAL_ADDRESS pa, 68acpi_find_table_rsdp(ACPI_PHYSICAL_ADDRESS pa,
69 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen) 69 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
70{ 70{
71 ACPI_PHYSICAL_ADDRESS table_pa; 71 ACPI_PHYSICAL_ADDRESS table_pa;
72 72
73 table_pa = AcpiOsGetRootPointer(); 73 table_pa = AcpiOsGetRootPointer();
74 if (table_pa == 0) { 74 if (table_pa == 0) {
75 return false; 75 return false;
76 } 76 }
77 if (pa >= table_pa && pa < table_pa + sizeof(ACPI_TABLE_RSDP)) { 77 if (pa >= table_pa && pa < table_pa + sizeof(ACPI_TABLE_RSDP)) {
78 *paddr = table_pa; 78 *paddr = table_pa;
79 *plen = sizeof(ACPI_TABLE_RSDP); 79 *plen = sizeof(ACPI_TABLE_RSDP);
80 return true; 80 return true;
81 } 81 }
82 82
83 return false; 83 return false;
84} 84}
85 85
86/* 86/*
87 * acpi_find_table_sdt -- 87 * acpi_find_table_sdt --
88 * 88 *
89 * Returns true if the XSDT/RSDT table is found and overlaps the 89 * Returns true if the XSDT/RSDT table is found and overlaps the
90 * specified physical address. The table's physical start address 90 * specified physical address. The table's physical start address
91 * and length are placed in 'paddr' and 'plen' when found. 91 * and length are placed in 'paddr' and 'plen' when found.
92 * 92 *
93 */ 93 */
94static bool 94static bool
95acpi_find_table_sdt(ACPI_PHYSICAL_ADDRESS pa, 95acpi_find_table_sdt(ACPI_PHYSICAL_ADDRESS pa,
96 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen) 96 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
97{ 97{
98 ACPI_PHYSICAL_ADDRESS table_pa; 98 ACPI_PHYSICAL_ADDRESS table_pa;
99 ACPI_TABLE_RSDP *rsdp; 99 ACPI_TABLE_RSDP *rsdp;
100 ACPI_TABLE_HEADER *thdr; 100 ACPI_TABLE_HEADER *thdr;
101 uint32_t table_len; 101 uint32_t table_len;
102 102
103 table_pa = AcpiOsGetRootPointer(); 103 table_pa = AcpiOsGetRootPointer();
104 KASSERT(table_pa != 0); 104 KASSERT(table_pa != 0);
105 105
106 /* 106 /*
107 * Find the XSDT/RSDT using the RSDP. 107 * Find the XSDT/RSDT using the RSDP.
108 */ 108 */
109 rsdp = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_RSDP)); 109 rsdp = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_RSDP));
110 if (rsdp == NULL) { 110 if (rsdp == NULL) {
111 return false; 111 return false;
112 } 112 }
113 if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress) { 113 if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress) {
114 table_pa = rsdp->XsdtPhysicalAddress; 114 table_pa = rsdp->XsdtPhysicalAddress;
115 } else { 115 } else {
116 table_pa = rsdp->RsdtPhysicalAddress; 116 table_pa = rsdp->RsdtPhysicalAddress;
117 } 117 }
118 AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP)); 118 AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP));
119 if (table_pa == 0) { 119 if (table_pa == 0) {
120 return false; 120 return false;
121 } 121 }
122 122
123 /* 123 /*
124 * Map the XSDT/RSDT and check access. 124 * Map the XSDT/RSDT and check access.
125 */ 125 */
126 thdr = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_HEADER)); 126 thdr = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_HEADER));
127 if (thdr == NULL) { 127 if (thdr == NULL) {
128 return false; 128 return false;
129 } 129 }
130 table_len = thdr->Length; 130 table_len = thdr->Length;
131 AcpiOsUnmapMemory(thdr, sizeof(ACPI_TABLE_HEADER)); 131 AcpiOsUnmapMemory(thdr, sizeof(ACPI_TABLE_HEADER));
132 if (pa >= table_pa && pa < table_pa + table_len) { 132 if (pa >= table_pa && pa < table_pa + table_len) {
133 *paddr = table_pa; 133 *paddr = table_pa;
134 *plen = table_len; 134 *plen = table_len;
135 return true; 135 return true;
136 } 136 }
137 137
138 return false; 138 return false;
139} 139}
140 140
141/* 141/*
142 * acpi_find_table -- 142 * acpi_find_table --
143 * 143 *
144 * Find an ACPI table that overlaps the specified physical address. 144 * Find an ACPI table that overlaps the specified physical address.
145 * Returns true if the table is found and places the table start 145 * Returns true if the table is found and places the table start
146 * address into 'paddr' and the length into 'plen'. 146 * address into 'paddr' and the length into 'plen'.
147 * 147 *
148 */ 148 */
149static bool 149static bool
150acpi_find_table(ACPI_PHYSICAL_ADDRESS pa, 150acpi_find_table(ACPI_PHYSICAL_ADDRESS pa,
151 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen) 151 ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
152{ 152{
153 ACPI_TABLE_DESC *tdesc; 153 ACPI_TABLE_DESC *tdesc;
154 bool found_table; 154 bool found_table;
155 uint32_t i; 155 uint32_t i;
156 156
157 /* Check for RSDP access. */ 157 /* Check for RSDP access. */
158 if (acpi_find_table_rsdp(pa, paddr, plen)) { 158 if (acpi_find_table_rsdp(pa, paddr, plen)) {
159 return true; 159 return true;
160 } 160 }
161 161
162 /* Check for XSDT/RSDT access. */ 162 /* Check for XSDT/RSDT access. */
163 if (acpi_find_table_sdt(pa, paddr, plen)) { 163 if (acpi_find_table_sdt(pa, paddr, plen)) {
164 return true; 164 return true;
165 } 165 }
166 166
167 /* Check for root table access. */ 167 /* Check for root table access. */
168 found_table = false; 168 found_table = false;
169 AcpiUtAcquireMutex(ACPI_MTX_TABLES); 169 AcpiUtAcquireMutex(ACPI_MTX_TABLES);
170 for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++) { 170 for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++) {
171 tdesc = &AcpiGbl_RootTableList.Tables[i]; 171 tdesc = &AcpiGbl_RootTableList.Tables[i];
172 if (pa >= tdesc->Address && 172 if (pa >= tdesc->Address &&
173 pa < tdesc->Address + tdesc->Length) { 173 pa < tdesc->Address + tdesc->Length) {
174 *paddr = tdesc->Address; 174 *paddr = tdesc->Address;
175 *plen = tdesc->Length; 175 *plen = tdesc->Length;
176 found_table = true; 176 found_table = true;
177 break; 177 break;
178 } 178 }
179 } 179 }
180 AcpiUtReleaseMutex(ACPI_MTX_TABLES); 180 AcpiUtReleaseMutex(ACPI_MTX_TABLES);
181 return found_table; 181 return found_table;
182} 182}
183 183
184/* 184/*
185 * acpi_read -- 185 * acpi_read --
186 * 186 *
187 * Read data from an ACPI configuration table that resides in 187 * Read data from an ACPI configuration table that resides in
188 * physical memory. Only supports reading one table at a time. 188 * physical memory. Only supports reading one table at a time.
189 * 189 *
190 */ 190 */
191static int 191static int
192acpi_read(dev_t dev, struct uio *uio, int flag) 192acpi_read(dev_t dev, struct uio *uio, int flag)
193{ 193{
194 ACPI_PHYSICAL_ADDRESS pa, table_pa; 194 ACPI_PHYSICAL_ADDRESS pa, table_pa;
195 uint32_t table_len; 195 uint32_t table_len;
196 uint8_t *data; 196 uint8_t *data;
197 int error; 197 int error;
198 size_t len; 198 size_t len;
199 199
200 if (uio->uio_rw != UIO_READ) { 200 if (uio->uio_rw != UIO_READ) {
201 return EPERM; 201 return EPERM;
202 } 202 }
203 203
204 /* Make sure this is a read of a known table */ 204 /* Make sure this is a read of a known table */
205 if (!acpi_find_table(uio->uio_offset, &table_pa, &table_len)) { 205 if (!acpi_find_table(uio->uio_offset, &table_pa, &table_len)) {
206 return EIO; 206 return EIO;
207 } 207 }
208 208
209 /* Copy the contents of the table to user-space */ 209 /* Copy the contents of the table to user-space */
210 pa = uio->uio_offset; 210 pa = uio->uio_offset;
211 len = uimin(pa - table_pa + table_len, uio->uio_resid); 211 len = uimin(table_len - (pa - table_pa), uio->uio_resid);
212 data = AcpiOsMapMemory(pa, len); 212 data = AcpiOsMapMemory(pa, len);
213 if (data == NULL) { 213 if (data == NULL) {
214 return ENOMEM; 214 return ENOMEM;
215 } 215 }
216 error = uiomove(data, len, uio); 216 error = uiomove(data, len, uio);
217 AcpiOsUnmapMemory(data, len); 217 AcpiOsUnmapMemory(data, len);
218 218
219 return error; 219 return error;
220} 220}