[PATCH v2] block: sed-opal: validate response token bounds
Pengpeng Hou <[email protected]>
| Newsgroups | org.kernel.vger.linux-block,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The OPAL response parser checks the containing subpacket but does
not prove each variable-width token is within that subpacket before
reading its header or payload. It can also append beyond the fixed token
metadata array.
Pass the remaining subpacket length to atom parsers, validate every
header and token extent, check the declared subpacket length against the
supplied response, and reject responses with more than MAX_TOKS tokens.
Fixes: 455a7b238cd6 ("block: Add Sed-opal library")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <[email protected]>
---
Changes since v1: https://lore.kernel.org/all/[email protected]/
- no source-code changes
- rebase on the current block sources and tighten the commit message
- add the coding-assistant disclosure
The token grammar and all parser reads were reviewed statically; no
malformed OPAL response was injected.
block/sed-opal.c | 49 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/block/sed-opal.c b/block/sed-opal.c
index 79b290d9458a..c0afd34f8e2f 100644
--- a/block/sed-opal.c
+++ b/block/sed-opal.c
@@ -936,12 +936,17 @@ static ssize_t response_parse_tiny(struct opal_resp_tok *tok,
}
static ssize_t response_parse_short(struct opal_resp_tok *tok,
- const u8 *pos)
+ const u8 *pos, size_t remaining)
{
tok->pos = pos;
tok->len = (pos[0] & SHORT_ATOM_LEN_MASK) + 1;
tok->width = OPAL_WIDTH_SHORT;
+ if (tok->len > remaining) {
+ pr_debug("Short atom exceeds response subpacket\n");
+ return -EINVAL;
+ }
+
if (pos[0] & SHORT_ATOM_BYTESTRING) {
tok->type = OPAL_DTA_TOKENID_BYTESTRING;
} else if (pos[0] & SHORT_ATOM_SIGNED) {
@@ -966,12 +971,22 @@ static ssize_t response_parse_short(struct opal_resp_tok *tok,
}
static ssize_t response_parse_medium(struct opal_resp_tok *tok,
- const u8 *pos)
+ const u8 *pos, size_t remaining)
{
+ if (remaining < 2) {
+ pr_debug("Truncated medium atom header\n");
+ return -EINVAL;
+ }
+
tok->pos = pos;
tok->len = (((pos[0] & MEDIUM_ATOM_LEN_MASK) << 8) | pos[1]) + 2;
tok->width = OPAL_WIDTH_MEDIUM;
+ if (tok->len > remaining) {
+ pr_debug("Medium atom exceeds response subpacket\n");
+ return -EINVAL;
+ }
+
if (pos[0] & MEDIUM_ATOM_BYTESTRING)
tok->type = OPAL_DTA_TOKENID_BYTESTRING;
else if (pos[0] & MEDIUM_ATOM_SIGNED)
@@ -983,12 +998,22 @@ static ssize_t response_parse_medium(struct opal_resp_tok *tok,
}
static ssize_t response_parse_long(struct opal_resp_tok *tok,
- const u8 *pos)
+ const u8 *pos, size_t remaining)
{
+ if (remaining < 4) {
+ pr_debug("Truncated long atom header\n");
+ return -EINVAL;
+ }
+
tok->pos = pos;
tok->len = ((pos[1] << 16) | (pos[2] << 8) | pos[3]) + 4;
tok->width = OPAL_WIDTH_LONG;
+ if (tok->len > remaining) {
+ pr_debug("Long atom exceeds response subpacket\n");
+ return -EINVAL;
+ }
+
if (pos[0] & LONG_ATOM_BYTESTRING)
tok->type = OPAL_DTA_TOKENID_BYTESTRING;
else if (pos[0] & LONG_ATOM_SIGNED)
@@ -1016,7 +1041,7 @@ static int response_parse(const u8 *buf, size_t length,
const struct opal_header *hdr;
struct opal_resp_tok *iter;
int num_entries = 0;
- int total;
+ size_t total;
ssize_t token_length;
const u8 *pos;
u32 clen, plen, slen;
@@ -1027,6 +1052,9 @@ static int response_parse(const u8 *buf, size_t length,
if (!resp)
return -EFAULT;
+ if (length < sizeof(*hdr))
+ return -EINVAL;
+
hdr = (struct opal_header *)buf;
pos = buf;
pos += sizeof(*hdr);
@@ -1038,10 +1066,10 @@ static int response_parse(const u8 *buf, size_t length,
clen, plen, slen);
if (clen == 0 || plen == 0 || slen == 0 ||
- slen > IO_BUFFER_LENGTH - sizeof(*hdr)) {
+ slen > length - sizeof(*hdr)) {
pr_debug("Bad header length. cp: %u, pkt: %u, subpkt: %u\n",
clen, plen, slen);
- print_buffer(pos, sizeof(*hdr));
+ print_buffer(buf, sizeof(*hdr));
return -EINVAL;
}
@@ -1052,14 +1080,17 @@ static int response_parse(const u8 *buf, size_t length,
total = slen;
print_buffer(pos, total);
while (total > 0) {
+ if (iter == resp->toks + MAX_TOKS)
+ return -E2BIG;
+
if (pos[0] <= TINY_ATOM_BYTE) /* tiny atom */
token_length = response_parse_tiny(iter, pos);
else if (pos[0] <= SHORT_ATOM_BYTE) /* short atom */
- token_length = response_parse_short(iter, pos);
+ token_length = response_parse_short(iter, pos, total);
else if (pos[0] <= MEDIUM_ATOM_BYTE) /* medium atom */
- token_length = response_parse_medium(iter, pos);
+ token_length = response_parse_medium(iter, pos, total);
else if (pos[0] <= LONG_ATOM_BYTE) /* long atom */
- token_length = response_parse_long(iter, pos);
+ token_length = response_parse_long(iter, pos, total);
else if (pos[0] == EMPTY_ATOM_BYTE) /* empty atom */
token_length = 1;
else /* TOKEN */
--
2.50.1 (Apple Git-155)