bcachefs: Guard against unknown compression options
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 22:29:54 +0000 (18:29 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Tue, 31 Oct 2023 16:18:37 +0000 (12:18 -0400)
Since compression options now include compression level, proper
validation is a bit more involved.

This adds bch2_compression_opt_valid(), and plumbs it around
appropriately.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/compress.c
fs/bcachefs/compress.h
fs/bcachefs/errcode.h
fs/bcachefs/inode.c
fs/bcachefs/opts.c
fs/bcachefs/opts.h

index 1480b64547b0c961d7a62f06071e8bffcf7e35a8..0e3981f42526104e57bc6156e32a5ea4efebd21b 100644 (file)
@@ -708,3 +708,13 @@ void bch2_opt_compression_to_text(struct printbuf *out,
        if (opt.level)
                prt_printf(out, ":%u", opt.level);
 }
+
+int bch2_opt_compression_validate(u64 v, struct printbuf *err)
+{
+       if (!bch2_compression_opt_valid(v)) {
+               prt_printf(err, "invalid compression opt %llu", v);
+               return -BCH_ERR_invalid_sb_opt_compression;
+       }
+
+       return 0;
+}
index 052ea303241fc31407edde0bcc2d3037d7691137..b938fc936365983951b63fc976cae87293244061 100644 (file)
@@ -4,12 +4,18 @@
 
 #include "extents_types.h"
 
+static const unsigned __bch2_compression_opt_to_type[] = {
+#define x(t, n) [BCH_COMPRESSION_OPT_##t] = BCH_COMPRESSION_TYPE_##t,
+       BCH_COMPRESSION_OPTS()
+#undef x
+};
+
 struct bch_compression_opt {
        u8              type:4,
                        level:4;
 };
 
-static inline struct bch_compression_opt bch2_compression_decode(unsigned v)
+static inline struct bch_compression_opt __bch2_compression_decode(unsigned v)
 {
        return (struct bch_compression_opt) {
                .type   = v & 15,
@@ -17,17 +23,25 @@ static inline struct bch_compression_opt bch2_compression_decode(unsigned v)
        };
 }
 
+static inline bool bch2_compression_opt_valid(unsigned v)
+{
+       struct bch_compression_opt opt = __bch2_compression_decode(v);
+
+       return opt.type < ARRAY_SIZE(__bch2_compression_opt_to_type) && !(!opt.type && opt.level);
+}
+
+static inline struct bch_compression_opt bch2_compression_decode(unsigned v)
+{
+       return bch2_compression_opt_valid(v)
+               ? __bch2_compression_decode(v)
+               : (struct bch_compression_opt) { 0 };
+}
+
 static inline unsigned bch2_compression_encode(struct bch_compression_opt opt)
 {
        return opt.type|(opt.level << 4);
 }
 
-static const unsigned __bch2_compression_opt_to_type[] = {
-#define x(t, n) [BCH_COMPRESSION_OPT_##t] = BCH_COMPRESSION_TYPE_##t,
-       BCH_COMPRESSION_OPTS()
-#undef x
-};
-
 static inline enum bch_compression_type bch2_compression_opt_to_type(unsigned v)
 {
        return __bch2_compression_opt_to_type[bch2_compression_decode(v).type];
@@ -46,10 +60,12 @@ int bch2_fs_compress_init(struct bch_fs *);
 
 int bch2_opt_compression_parse(struct bch_fs *, const char *, u64 *, struct printbuf *);
 void bch2_opt_compression_to_text(struct printbuf *, struct bch_fs *, struct bch_sb *, u64);
+int bch2_opt_compression_validate(u64, struct printbuf *);
 
 #define bch2_opt_compression (struct bch_opt_fn) {             \
-       .parse          = bch2_opt_compression_parse,   \
-       .to_text        = bch2_opt_compression_to_text, \
+       .parse          = bch2_opt_compression_parse,           \
+       .to_text        = bch2_opt_compression_to_text,         \
+       .validate       = bch2_opt_compression_validate,        \
 }
 
 #endif /* _BCACHEFS_COMPRESS_H */
index 7cc083776a2e029a6ec2b11169867e4b02e1173d..3e9f09cea6c799477193cc535b33fb32462e13f4 100644 (file)
        x(BCH_ERR_invalid_sb,           invalid_sb_crypt)                       \
        x(BCH_ERR_invalid_sb,           invalid_sb_clean)                       \
        x(BCH_ERR_invalid_sb,           invalid_sb_quota)                       \
+       x(BCH_ERR_invalid_sb,           invalid_sb_opt_compression)             \
        x(BCH_ERR_invalid,              invalid_bkey)                           \
        x(BCH_ERR_operation_blocked,    nocow_lock_blocked)                     \
        x(EIO,                          btree_node_read_err)                    \
index bb3f443d8381cc1dd087e961a593a989146e6014..a3921c397ea254896dc19bd44e2f1cd715da34f1 100644 (file)
@@ -6,6 +6,7 @@
 #include "bkey_methods.h"
 #include "btree_update.h"
 #include "buckets.h"
+#include "compress.h"
 #include "error.h"
 #include "extents.h"
 #include "extent_update.h"
@@ -422,9 +423,10 @@ static int __bch2_inode_invalid(struct bkey_s_c k, struct printbuf *err)
                return -BCH_ERR_invalid_bkey;
        }
 
-       if (unpacked.bi_compression >= BCH_COMPRESSION_OPT_NR + 1) {
-               prt_printf(err, "invalid data checksum type (%u >= %u)",
-                      unpacked.bi_compression, BCH_COMPRESSION_OPT_NR + 1);
+       if (unpacked.bi_compression &&
+           !bch2_compression_opt_valid(unpacked.bi_compression - 1)) {
+               prt_printf(err, "invalid compression opt %u",
+                          unpacked.bi_compression - 1);
                return -BCH_ERR_invalid_bkey;
        }
 
index 8294f56e45d5a503821e1bf1d15539f3bee8ae89..b7722b6236978cd692a478cd4ec324ade4456bb4 100644 (file)
@@ -294,6 +294,9 @@ int bch2_opt_validate(const struct bch_option *opt, u64 v, struct printbuf *err)
                return -EINVAL;
        }
 
+       if (opt->fn.validate)
+               return opt->fn.validate(v, err);
+
        return 0;
 }
 
index 16dd0f0622bcbbbb72d3222810b603a12b3ce5ad..2307cdd2a23cd18ad324d223aefef231ea7cb857 100644 (file)
@@ -74,6 +74,7 @@ enum opt_type {
 struct bch_opt_fn {
        int (*parse)(struct bch_fs *, const char *, u64 *, struct printbuf *);
        void (*to_text)(struct printbuf *, struct bch_fs *, struct bch_sb *, u64);
+       int (*validate)(u64, struct printbuf *);
 };
 
 /**