From: Darrick J. Wong <djwong@xxxxxxxxxx> Validate that the event objects that we get from the kernel actually obey the schema that the kernel publishes. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- libxfs/Makefile | 10 ++++++-- scrub/Makefile | 1 + scrub/xfs_scrubbed.in | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/libxfs/Makefile b/libxfs/Makefile index 61c43529b532b6..f84eb5b43cdddd 100644 --- a/libxfs/Makefile +++ b/libxfs/Makefile @@ -151,6 +151,8 @@ EXTRA_OBJECTS=\ LDIRT += $(EXTRA_OBJECTS) +JSON_SCHEMAS=xfs_healthmon.schema.json + # # Tracing flags: # -DMEM_DEBUG all zone memory use @@ -174,7 +176,7 @@ LTLIBS = $(LIBPTHREAD) $(LIBRT) # don't try linking xfs_repair with a debug libxfs. DEBUG = -DNDEBUG -default: ltdepend $(LTLIBRARY) $(EXTRA_OBJECTS) +default: ltdepend $(LTLIBRARY) $(EXTRA_OBJECTS) $(JSON_SCHEMAS) %dummy.o: %dummy.cpp @echo " [CXXD] $@" @@ -196,14 +198,16 @@ MAKECXXDEP := $(MAKEDEPEND) $(CXXFLAGS) include $(BUILDRULES) install: default - $(INSTALL) -m 755 -d $(PKG_INC_DIR) + $(INSTALL) -m 755 -d $(PKG_DATA_DIR) + $(INSTALL) -m 644 $(JSON_SCHEMAS) $(PKG_DATA_DIR) install-headers: $(addsuffix -hdrs, $(PKGHFILES)) %-hdrs: $(Q)$(LN_S) -f $(CURDIR)/$* $(TOPDIR)/include/xfs/$* -install-dev: install +install-dev: default + $(INSTALL) -m 755 -d $(PKG_INC_DIR) $(INSTALL) -m 644 $(PKGHFILES) $(PKG_INC_DIR) # We need to install the headers before building the dependencies. If we diff --git a/scrub/Makefile b/scrub/Makefile index bd910922ceb4bb..7d4fa0ddc09685 100644 --- a/scrub/Makefile +++ b/scrub/Makefile @@ -129,6 +129,7 @@ xfs_scrubbed: xfs_scrubbed.in $(builddefs) $(Q)$(SED) -e "s|@sbindir@|$(PKG_SBIN_DIR)|g" \ -e "s|@scrub_svcname@|$(scrub_svcname)|g" \ -e "s|@pkg_version@|$(PKG_VERSION)|g" \ + -e "s|@pkg_data_dir@|$(PKG_DATA_DIR)|g" \ < $< > $@ $(Q)chmod a+x $@ diff --git a/scrub/xfs_scrubbed.in b/scrub/xfs_scrubbed.in index 4d742a9151a082..992797113d6d30 100644 --- a/scrub/xfs_scrubbed.in +++ b/scrub/xfs_scrubbed.in @@ -18,6 +18,52 @@ import ctypes import gc from concurrent.futures import ProcessPoolExecutor +try: + # Not all systems will have this json schema validation libarary, + # so we make it optional. + import jsonschema + + def init_validation(args): + '''Initialize event json validation.''' + try: + with open(args.event_schema) as fp: + schema_js = json.load(fp) + except Exception as e: + print(f"{args.event_schema}: {e}", file = sys.stderr) + return + + try: + vcls = jsonschema.validators.validator_for(schema_js) + vcls.check_schema(schema_js) + validator = vcls(schema_js) + except jsonschema.exceptions.SchemaError as e: + print(f"{args.event_schema}: invalid event data, {e.message}", + file = sys.stderr) + return + except Exception as e: + print(f"{args.event_schema}: {e}", file = sys.stderr) + return + + def v(i): + e = jsonschema.exceptions.best_match(validator.iter_errors(i)) + if e: + print(f"{printf_prefix}: {e.message}", + file = sys.stderr) + return False + return True + + return v + +except: + def init_validation(args): + if args.require_validation: + print("JSON schema validation not available.", + file = sys.stderr) + return + + return lambda instance: True + +validator_fn = None debug = False log = False everything = False @@ -177,6 +223,12 @@ def handle_event(event): global log + # Ignore any event that doesn't pass our schema. This program must + # not try to handle a newer kernel that say things that it is not + # prepared to handle. + if not validator_fn(event): + return + stringify_timestamp(event) if log: log_event(event) @@ -225,6 +277,7 @@ def main(): global printf_prefix global everything global debug_fast + global validator_fn parser = argparse.ArgumentParser( \ description = "XFS filesystem health monitoring demon.") @@ -240,6 +293,11 @@ def main(): help = 'XFS filesystem mountpoint to target.') parser.add_argument('--debug-fast', action = 'store_true', \ help = argparse.SUPPRESS) + parser.add_argument('--require-validation', action = 'store_true', \ + help = argparse.SUPPRESS) + parser.add_argument('--event-schema', type = str, \ + default = '@pkg_data_dir@/xfs_healthmon.schema.json', \ + help = argparse.SUPPRESS) args = parser.parse_args() if args.V: @@ -250,6 +308,10 @@ def main(): parser.error("the following arguments are required: mountpoint") return 1 + validator_fn = init_validation(args) + if not validator_fn: + return 1 + if args.debug: debug = True if args.log: