From 6cb0e025b973c60bcaa1506b92087dd963e45d31 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 4 Oct 2024 13:12:13 -0600 Subject: lib9p: Improve handling of stat object sizes --- Makefile | 2 +- lib9p/9p.gen | 37 +++++++++++++++++++++++++++++++++---- lib9p/9p.generated.c | 21 ++++++++++++++++----- lib9p/include/lib9p/9p.generated.h | 1 - 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index ffa9778..3a260d6 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ generate-clean: sources_sh = 3rd-party/linux-errno.txt.gen sources_sh += libusb/include/libusb/tusb_helpers.h.gen -sources_py = lib9p/types.gen +sources_py = lib9p/9p.gen sources_py += lib9p/include/lib9p/linux-errno.h.gen lint: shellcheck $(sources_sh) diff --git a/lib9p/9p.gen b/lib9p/9p.gen index 6e01b17..6f2ad2c 100755 --- a/lib9p/9p.gen +++ b/lib9p/9p.gen @@ -101,9 +101,7 @@ class Member: re_membername = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" -re_memberspec = ( - f"(?:(?P{re_membername})\\*\\()?(?P{re_membername})\\[(?P[^,]*)(?:,max=(?P[0-9]+))?\\]\\)?" -) +re_memberspec = f"(?:(?P{re_membername})\\*\\()?(?P{re_membername})\\[(?P[^,]*)(?:,max=(?P[0-9]+))?\\]\\)?" def parse_members( @@ -138,7 +136,9 @@ def parse_members( if maxstr := m.group("max"): if (not isinstance(member.typ, Atom)) or member.cnt: - raise ValueError(f"',max=' may only be specified on a non-repeated atom") + raise ValueError( + f"',max=' may only be specified on a non-repeated atom" + ) member.max = int(maxstr) ret += [member] @@ -431,6 +431,8 @@ enum {idprefix}version {{ ret += "\n" ret += c_typename(idprefix, struct) + " {\n" for member in struct.members: + if struct.name == "stat" and member.name == "stat_size": # SPECIAL + continue ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};" if (not all_the_same) and (comment := c_vercomment(member.ver)): ret += (" " * (namewidth - len(member.name))) + " " + comment @@ -655,6 +657,9 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, ret += "}\n" continue + if typ.name == "stat": # SPECIAL + ret += f"\n\tuint32_t size_offset = ctx->net_offset;" + prefix0 = "\treturn " prefix1 = "\t || " prefix2 = "\t " @@ -673,6 +678,7 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, else: ret += f"validate_{member.typ.name}(ctx)" if member.max: + assert member.static_size ret += f"\n{prefix1}(decode_u{member.static_size*8}le(&ctx->net_bytes[ctx->net_offset-{member.static_size}]) > ({c_typename(idprefix, member.typ)})({member.max})" ret += f'\n{prefix2}\t? lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%"PRIu{member.static_size*8}" > %"PRIu{member.static_size*8}")",' ret += f"\n{prefix2}\t\tdecode_u{member.static_size*8}le(&ctx->net_bytes[ctx->net_offset-{member.static_size}]), ({c_typename(idprefix, member.typ)})({member.max}))" @@ -681,6 +687,11 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, ret += " )" prefix = prefix1 prev_size = member.static_size + if typ.name == "stat": # SPECIAL + assert typ.members[0].static_size + ret += f"\n{prefix1}((uint32_t)decode_u{typ.members[0].static_size*8}le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset)" + ret += f'\n{prefix2}\t? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents")"' + ret += f"\n{prefix2}\t: false)" ret += ";\n" ret += "}\n" @@ -726,6 +737,9 @@ static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) if typ.members: struct_versions = typ.members[0].ver for member in typ.members: + if typ.name == "stat" and member.name == "stat_size": # SPECIAL + ret += f"\tsize->net_offset += {member.static_size};\n" + continue ret += "\t" prefix = "\t" if member.ver != struct_versions: @@ -809,6 +823,9 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { ret += "}\n" continue + if typ.name == "stat": # SPECIAL + ret += "\n\tuint32_t size_offset = ctx->net_offset;" + prefix0 = "\treturn " prefix1 = "\t || " prefix2 = "\t " @@ -816,6 +833,12 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { struct_versions = typ.members[0].ver prefix = prefix0 for member in typ.members: + if typ.name == "stat" and member.name == "stat_size": # SPECIAL: + assert member.static_size + ret += f"\n{prefix }((ctx->net_offset + {member.static_size} > ctx->ctx->max_msg_size)" + ret += f"\n{prefix2}\t? _marshal_too_large(ctx)" + ret += f"\n{prefix2}\t: ({{ ctx->net_offset += {member.static_size}; false; }}))" + continue ret += f"\n{prefix}" if member.ver != struct_versions: ret += "( " + c_vercond(idprefix, member.ver) + " && " @@ -831,6 +854,12 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { if member.ver != struct_versions: ret += " )" prefix = prefix1 + if typ.name == "stat": # SPECIAL + assert typ.members[0].static_size + ret += f"\n{prefix1}((ctx->net_offset - size_offset > UINT16_MAX)" + ret += f'\n{prefix2}\t? lib9p_error(ctx->ctx, LINUX_ERANGE, "stat object too large")' + ret += f"\n{prefix2}\t: ({{ encode_u{typ.members[0].static_size*8}le((uint{typ.members[0].static_size*8}_t)(ctx->net_offset - size_offset), &ctx->net_bytes[size_offset]);" + ret += f"\n{prefix2} false; }}))" ret += ";\n" ret += "}\n" diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index 0259e23..4ec5ae5 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -383,6 +383,7 @@ static ALWAYS_INLINE bool validate_qid(struct _validate_ctx *ctx) { } static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { + uint32_t size_offset = ctx->net_offset; return validate_2(ctx) || validate_2(ctx) || validate_4(ctx) @@ -398,7 +399,10 @@ static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_s(ctx) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) + || ((uint32_t)decode_u16le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset) + ? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents")" + : false); } static ALWAYS_INLINE bool validate_o(struct _validate_ctx *ctx) { @@ -638,7 +642,7 @@ static ALWAYS_INLINE void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p static ALWAYS_INLINE void unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) { memset(out, 0, sizeof(*out)); - unmarshal_2(ctx, &out->stat_size); + size->net_offset += 2; unmarshal_2(ctx, &out->kern_type); unmarshal_4(ctx, &out->kern_dev); unmarshal_qid(ctx, &out->file_qid); @@ -938,8 +942,11 @@ static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid } static ALWAYS_INLINE bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { - return marshal_2(ctx, &val->stat_size) - || marshal_2(ctx, &val->kern_type) + uint32_t size_offset = ctx->net_offset; + return ((ctx->net_offset + 2 > ctx->ctx->max_msg_size) + ? _marshal_too_large(ctx) + : ({ ctx->net_offset += 2; false; })) + return marshal_2(ctx, &val->kern_type) || marshal_4(ctx, &val->kern_dev) || marshal_qid(ctx, &val->file_qid) || marshal_dm(ctx, &val->file_mode) @@ -953,7 +960,11 @@ static ALWAYS_INLINE bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_st || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_s(ctx, &val->file_extension) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_uid) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_gid) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_last_modified_n_uid) ); + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_last_modified_n_uid) ) + || ((ctx->net_offset - size_offset > UINT16_MAX) + ? lib9p_error(ctx->ctx, LINUX_ERANGE, "stat object too large") + : ({ encode_u16le((uint16_t)(ctx->net_offset - size_offset), &ctx->net_bytes[size_offset]); + false; })); } static ALWAYS_INLINE bool marshal_o(struct _marshal_ctx *ctx, lib9p_o_t *val) { diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h index c6aaee9..f093aa1 100644 --- a/lib9p/include/lib9p/9p.generated.h +++ b/lib9p/include/lib9p/9p.generated.h @@ -96,7 +96,6 @@ struct lib9p_qid { }; struct lib9p_stat { - uint16_t stat_size; uint16_t kern_type; uint32_t kern_dev; struct lib9p_qid file_qid; -- cgit v1.2.3-2-g168b