summaryrefslogtreecommitdiff
path: root/lib9p
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-02 22:09:31 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-02 22:09:31 -0600
commit4d451f90eba193a4ca2848c4ac7acccda96ed008 (patch)
treee00a41587de365134032fbd1ceff124b5552a280 /lib9p
parentad2027ae26e5d83eb42c9edaa90f7b278f9b0d3d (diff)
lib9p: Validate Twalk and Rwalk list sizes
Diffstat (limited to 'lib9p')
-rw-r--r--lib9p/9P2000.txt6
-rw-r--r--lib9p/types.c14
-rwxr-xr-xlib9p/types.gen13
3 files changed, 23 insertions, 10 deletions
diff --git a/lib9p/9P2000.txt b/lib9p/9P2000.txt
index 5b5b942..fa04fe0 100644
--- a/lib9p/9P2000.txt
+++ b/lib9p/9P2000.txt
@@ -115,7 +115,7 @@ bitfield o 8
READ = 0 # unlock read()
WRITE = 1 # unlock write()
RDWR = 2 # unlock read() and write()
- EXEC = 3 # unlock read() for files, walk() for directorie
+ EXEC = 3 # unlock read() for files, walk() for directories
# In the 9P protocol, each message has a type, and message types come
# in pairs (except "Rerror"); "T" and "R"; T-messages are
@@ -133,8 +133,8 @@ bitfield o 8
107/Rerror = "ename[s]"
108/Tflush = "oldtag[2]"
109/Rflush = ""
-110/Twalk = "fid[4] newfid[4] nwname[2] nwname*(wname[s])"
-111/Rwalk = "nwqid[2] nwqid*(wqid[qid])"
+110/Twalk = "fid[4] newfid[4] nwname[2] nwname*(wname[s])" # nwname has a maximum of 16
+111/Rwalk = "nwqid[2] nwqid*(wqid[qid])" # nwname has a maximum of 16
112/Topen = "fid[4] mode[o]"
113/Ropen = "qid[qid] iounit[4]"
114/Tcreate = "fid[4] name[s] perm[dm] mode[o]"
diff --git a/lib9p/types.c b/lib9p/types.c
index baddab3..0343a48 100644
--- a/lib9p/types.c
+++ b/lib9p/types.c
@@ -311,7 +311,11 @@ static ALWAYS_INLINE bool _validate_size_host(struct _validate_ctx *ctx, size_t
}
static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
- size_t cnt, _validate_fn_t item_fn, size_t item_host_size) {
+ size_t cnt, size_t max,
+ _validate_fn_t item_fn, size_t item_host_size) {
+ if (max && cnt > max)
+ return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)",
+ cnt, max);
for (size_t i = 0; i < cnt; i++)
if (_validate_size_host(ctx, item_host_size) || item_fn(ctx))
return true;
@@ -460,12 +464,12 @@ static FLATTEN bool validate_Twalk(struct _validate_ctx *ctx) {
return validate_4(ctx)
|| validate_4(ctx)
|| validate_2(ctx)
- || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s));
+ || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_s, sizeof(struct lib9p_s));
}
static FLATTEN bool validate_Rwalk(struct _validate_ctx *ctx) {
return validate_2(ctx)
- || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_qid, sizeof(struct lib9p_qid));
+ || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_qid, sizeof(struct lib9p_qid));
}
static FLATTEN bool validate_Topen(struct _validate_ctx *ctx) {
@@ -554,7 +558,7 @@ static FLATTEN bool validate_Rsession(struct _validate_ctx *UNUSED(ctx)) {
static FLATTEN bool validate_Tsread(struct _validate_ctx *ctx) {
return validate_4(ctx)
|| validate_2(ctx)
- || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s));
+ || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s));
}
static FLATTEN bool validate_Rsread(struct _validate_ctx *ctx) {
@@ -564,7 +568,7 @@ static FLATTEN bool validate_Rsread(struct _validate_ctx *ctx) {
static FLATTEN bool validate_Tswrite(struct _validate_ctx *ctx) {
return validate_4(ctx)
|| validate_2(ctx)
- || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s))
+ || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s))
|| validate_d(ctx);
}
diff --git a/lib9p/types.gen b/lib9p/types.gen
index da99c36..8b1b878 100755
--- a/lib9p/types.gen
+++ b/lib9p/types.gen
@@ -551,7 +551,11 @@ static ALWAYS_INLINE bool _validate_size_host(struct _validate_ctx *ctx, size_t
}
static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
- size_t cnt, _validate_fn_t item_fn, size_t item_host_size) {
+ size_t cnt, size_t max,
+ _validate_fn_t item_fn, size_t item_host_size) {
+ if (max && cnt > max)
+ return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)",
+ cnt, max);
for (size_t i = 0; i < cnt; i++)
if (_validate_size_host(ctx, item_host_size) || item_fn(ctx))
return true;
@@ -663,7 +667,12 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
ret += "( " + c_vercond(idprefix, member.ver) + " && "
if member.cnt is not None:
assert prev_size
- ret += f"_validate_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), validate_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}))"
+ maxelem = 0
+ if (
+ typ.name in ["Twalk", "Rwalk"] and member.name[:1] == "w"
+ ): # SPECIAL
+ maxelem = 16
+ ret += f"_validate_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), {maxelem}, validate_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}))"
else:
ret += f"validate_{member.typ.name}(ctx)"
if member.ver != struct_versions: