Skip to content

Commit 2d38d14

Browse files
committed
Kernel: Validate ACPI checksums (and skip invalid tables)
1 parent 8604f11 commit 2d38d14

File tree

2 files changed

+72
-15
lines changed

2 files changed

+72
-15
lines changed

Kernel/Arch/x86_64/Firmware/ACPI.cpp

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,66 @@
66

77
#include <Kernel/Arch/x86_64/Firmware/ACPI.h>
88
#include <Kernel/Arch/x86_64/Firmware/PCBIOS/Mapper.h>
9+
#include <Kernel/Firmware/ACPI/Definitions.h>
910
#include <Kernel/Memory/MemoryManager.h>
1011

1112
namespace Kernel::ACPI::StaticParsing {
1213

14+
static bool is_rsdp_valid(u8 const* rsdp, size_t region_size)
15+
{
16+
if (region_size < sizeof(Structures::RSDPDescriptor))
17+
return false;
18+
19+
u8 revision = reinterpret_cast<Structures::RSDPDescriptor const*>(rsdp)->revision;
20+
21+
u8 checksum = 0;
22+
for (size_t i = 0; i < sizeof(Structures::RSDPDescriptor); ++i)
23+
checksum += rsdp[i];
24+
25+
if (checksum != 0)
26+
return false;
27+
28+
if (revision == 0)
29+
// Checksum matched and there's nothing more to check.
30+
return true;
31+
32+
if (region_size < sizeof(Structures::RSDPDescriptor20))
33+
return false;
34+
35+
checksum = 0;
36+
for (size_t i = 0; i < sizeof(Structures::RSDPDescriptor20); ++i)
37+
checksum += rsdp[i];
38+
return checksum == 0;
39+
}
40+
1341
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#finding-the-rsdp-on-ia-pc-systems
1442
Optional<PhysicalAddress> find_rsdp_in_ia_pc_specific_memory_locations()
1543
{
1644
constexpr auto signature = "RSD PTR "sv;
45+
46+
auto locate_rsdp = [&signature](Memory::MappedROM mapping) -> Optional<PhysicalAddress> {
47+
while (true) {
48+
u8 const* rsdp = mapping.pointer_to_chunk_starting_with(signature, 16);
49+
if (!rsdp)
50+
return {};
51+
if (is_rsdp_valid(rsdp, static_cast<size_t>(mapping.end() - rsdp)))
52+
return mapping.paddr_of(rsdp);
53+
mapping.offset = (mapping.paddr_of(rsdp).get() - mapping.paddr.get()) + signature.length();
54+
mapping.size -= static_cast<size_t>(rsdp - mapping.base()) + signature.length();
55+
}
56+
};
57+
1758
auto ebda_or_error = map_ebda();
1859
if (!ebda_or_error.is_error()) {
19-
auto rsdp = ebda_or_error.value().find_chunk_starting_with(signature, 16);
20-
if (rsdp.has_value())
21-
return rsdp;
60+
auto maybe_rsdp = locate_rsdp(ebda_or_error.release_value());
61+
if (maybe_rsdp.has_value())
62+
return maybe_rsdp.value();
2263
}
2364
auto bios_or_error = map_bios();
2465
if (!bios_or_error.is_error()) {
25-
auto rsdp = bios_or_error.value().find_chunk_starting_with(signature, 16);
26-
if (rsdp.has_value())
27-
return rsdp;
66+
auto maybe_rsdp = locate_rsdp(bios_or_error.release_value());
67+
if (maybe_rsdp.has_value())
68+
return maybe_rsdp.value();
2869
}
2970

3071
// On some systems the RSDP may be located in ACPI NVS or reclaimable memory regions
@@ -52,9 +93,9 @@ Optional<PhysicalAddress> find_rsdp_in_ia_pc_specific_memory_locations()
5293
mapping.size = memory_range.length;
5394
mapping.paddr = memory_range.start;
5495

55-
auto rsdp = mapping.find_chunk_starting_with(signature, 16);
56-
if (rsdp.has_value())
57-
return rsdp;
96+
auto maybe_rsdp = locate_rsdp(move(mapping));
97+
if (maybe_rsdp.has_value())
98+
return maybe_rsdp.value();
5899
}
59100

60101
return {};

Kernel/Firmware/ACPI/StaticParsing.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

7+
#include <AK/ByteReader.h>
78
#include <Kernel/Firmware/ACPI/Definitions.h>
89
#include <Kernel/Firmware/ACPI/StaticParsing.h>
910
#include <Kernel/Library/StdLib.h>
@@ -31,16 +32,33 @@ Optional<PhysicalAddress> find_rsdp()
3132

3233
static bool match_table_signature(PhysicalAddress table_header, StringView signature)
3334
{
34-
// FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
3535
VERIFY(signature.length() == 4);
3636

37-
auto table = Memory::map_typed<Structures::RSDT>(table_header).release_value_but_fixme_should_propagate_errors();
38-
return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4);
37+
auto check_in_region = [&](size_t length, auto&& callback) -> bool {
38+
auto region = Memory::map_typed<u8 const>(table_header, length).release_value_but_fixme_should_propagate_errors();
39+
return callback(region.ptr());
40+
};
41+
42+
u32 length = 0;
43+
auto check_signature_and_get_length = [&](u8 const* base) -> bool {
44+
if (memcmp(reinterpret_cast<char const*>(base), signature.characters_without_null_termination(), 4) != 0)
45+
return false;
46+
length = ByteReader::load32(base + 4);
47+
return true;
48+
};
49+
50+
auto validate_checksum = [&](u8 const* base) -> bool {
51+
u8 checksum = 0;
52+
for (u32 i = 0; i < length; ++i)
53+
checksum += base[i];
54+
return checksum == 0;
55+
};
56+
57+
return check_in_region(8, check_signature_and_get_length) && check_in_region(length, validate_checksum);
3958
}
4059

4160
ErrorOr<Optional<PhysicalAddress>> search_table_in_xsdt(PhysicalAddress xsdt_address, StringView signature)
4261
{
43-
// FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
4462
VERIFY(signature.length() == 4);
4563

4664
auto xsdt = TRY(Memory::map_typed<Structures::XSDT>(xsdt_address));
@@ -54,7 +72,6 @@ ErrorOr<Optional<PhysicalAddress>> search_table_in_xsdt(PhysicalAddress xsdt_add
5472

5573
ErrorOr<Optional<PhysicalAddress>> find_table(PhysicalAddress rsdp_address, StringView signature)
5674
{
57-
// FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
5875
VERIFY(signature.length() == 4);
5976

6077
auto rsdp = TRY(Memory::map_typed<Structures::RSDPDescriptor20>(rsdp_address));
@@ -72,7 +89,6 @@ ErrorOr<Optional<PhysicalAddress>> find_table(PhysicalAddress rsdp_address, Stri
7289

7390
ErrorOr<Optional<PhysicalAddress>> search_table_in_rsdt(PhysicalAddress rsdt_address, StringView signature)
7491
{
75-
// FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
7692
VERIFY(signature.length() == 4);
7793

7894
auto rsdt = TRY(Memory::map_typed<Structures::RSDT>(rsdt_address));

0 commit comments

Comments
 (0)