diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc index e84babe421..ab069a3708 100644 --- a/docs/manual/manual.adoc +++ b/docs/manual/manual.adoc @@ -1618,6 +1618,7 @@ not considered local by the scanner: * `SEXP_VALIDATE_DISABLE` - If set, `oscap` will not validate SEXP expressions during its execution. * `SOURCE_DATE_EPOCH` - Timestamp in seconds since epoch. This timestamp will be used instead of the current time to populate `timestamp` attributes in SCAP source data streams created by `oscap ds sds-compose` sub-module. This is used for reproducible builds of data streams. * `OSCAP_PROBE_MEMORY_USAGE_RATIO` - maximum memory usage ratio (used/total) for OpenSCAP probes, default: 0.1 +* `OSCAP_PROBE_MAX_COLLECTED_ITEMS` - maximal count of collected items by OpenSCAP probe for a single OVAL object evaluation Also, OpenSCAP uses `libcurl` library which also can be configured using environment variables. See https://curl.se/libcurl/c/libcurl-env.html[the list of libcurl environment variables]. diff --git a/src/OVAL/probes/independent/textfilecontent54_probe.c b/src/OVAL/probes/independent/textfilecontent54_probe.c index d3a5c6eb22..4c6c9a1fa1 100644 --- a/src/OVAL/probes/independent/textfilecontent54_probe.c +++ b/src/OVAL/probes/independent/textfilecontent54_probe.c @@ -240,11 +240,14 @@ static int process_file(const char *prefix, const char *path, const char *file, item = create_item(path, file, pfd->pattern, cur_inst, substrs, substr_cnt, over); - probe_item_collect(pfd->ctx, item); - for (k = 0; k < substr_cnt; ++k) free(substrs[k]); free(substrs); + int pic_ret = probe_item_collect(pfd->ctx, item); + if (pic_ret == 2 || pic_ret == -1) { + ret = -4; + break; + } } } } while (substr_cnt > 0 && ofs < buf_used); diff --git a/src/OVAL/probes/probe/icache.c b/src/OVAL/probes/probe/icache.c index 6a18b62af5..45936a892a 100644 --- a/src/OVAL/probes/probe/icache.c +++ b/src/OVAL/probes/probe/icache.c @@ -39,6 +39,7 @@ #include "probe-api.h" #include "common/debug_priv.h" #include "common/memusage.h" +#include "oscap_helpers.h" #include "probe.h" #include "icache.h" @@ -528,6 +529,30 @@ static int probe_cobj_memcheck(size_t item_cnt, double max_ratio) return (0); } +static int _mark_collected_object_as_incomplete(struct probe_ctx *ctx, const char *message) +{ + /* + * Don't set the message again if the collected object is + * already flagged as incomplete. + */ + if (probe_cobj_get_flag(ctx->probe_out) == SYSCHAR_FLAG_INCOMPLETE) { + return 0; + } + /* + * Sync with the icache thread before modifying the + * collected object. + */ + if (probe_icache_nop(ctx->icache) != 0) { + return -1; + } + + SEXP_t *sexp_msg = probe_msg_creat(OVAL_MESSAGE_LEVEL_WARNING, (char *) message); + probe_cobj_add_msg(ctx->probe_out, sexp_msg); + probe_cobj_set_flag(ctx->probe_out, SYSCHAR_FLAG_INCOMPLETE); + SEXP_free(sexp_msg); + return 0; +} + /** * Collect an item * This function adds an item the collected object assosiated @@ -557,6 +582,16 @@ int probe_item_collect(struct probe_ctx *ctx, SEXP_t *item) cobj_itemcnt = SEXP_list_length(cobj_content); SEXP_free(cobj_content); + if (ctx->max_collected_items != OSCAP_PROBE_COLLECT_UNLIMITED && cobj_itemcnt >= ctx->max_collected_items) { + char *message = oscap_sprintf("Object is incomplete because the object matches more than %ld items.", ctx->max_collected_items); + if (_mark_collected_object_as_incomplete(ctx, message) != 0) { + free(message); + return -1; + } + free(message); + return 2; + } + memcheck_ret = probe_cobj_memcheck(cobj_itemcnt, ctx->max_mem_ratio); if (memcheck_ret == -1) { dE("Failed to check available memory"); @@ -565,27 +600,9 @@ int probe_item_collect(struct probe_ctx *ctx, SEXP_t *item) } if (memcheck_ret == 1) { SEXP_free(item); - - /* - * Don't set the message again if the collected object is - * already flagged as incomplete. - */ - if (probe_cobj_get_flag(ctx->probe_out) != SYSCHAR_FLAG_INCOMPLETE) { - SEXP_t *msg; - /* - * Sync with the icache thread before modifying the - * collected object. - */ - if (probe_icache_nop(ctx->icache) != 0) - return -1; - - msg = probe_msg_creat(OVAL_MESSAGE_LEVEL_WARNING, - "Object is incomplete due to memory constraints."); - - probe_cobj_add_msg(ctx->probe_out, msg); - probe_cobj_set_flag(ctx->probe_out, SYSCHAR_FLAG_INCOMPLETE); - - SEXP_free(msg); + const char *message = "Object is incomplete due to memory constraints."; + if (_mark_collected_object_as_incomplete(ctx, message) != 0) { + return -1; } return 2; diff --git a/src/OVAL/probes/probe/probe.h b/src/OVAL/probes/probe/probe.h index d3a488c4d5..20244fab27 100644 --- a/src/OVAL/probes/probe/probe.h +++ b/src/OVAL/probes/probe/probe.h @@ -43,6 +43,15 @@ #include "common/util.h" #include "common/compat_pthread_barrier.h" +/* default max. memory usage ratio - used/total */ +/* can be overridden by environment variable OSCAP_PROBE_MEMORY_USAGE_RATIO */ +#define OSCAP_PROBE_MEMORY_USAGE_RATIO_DEFAULT 0.33 + +/* By default, probes can collect unlimited amount of items. Ths behavior can + * be overridden by environment variable OSCAP_PROBE_MAX_COLLECTED_ITEMS. + */ +#define OSCAP_PROBE_COLLECT_UNLIMITED 0 + typedef struct { pthread_rwlock_t rwlock; uint32_t flags; @@ -84,6 +93,7 @@ struct probe_ctx { probe_icache_t *icache; /**< item cache */ int offline_mode; double max_mem_ratio; + size_t max_collected_items; }; typedef enum { diff --git a/src/OVAL/probes/probe/worker.c b/src/OVAL/probes/probe/worker.c index fddc1b3965..526bbbe78e 100644 --- a/src/OVAL/probes/probe/worker.c +++ b/src/OVAL/probes/probe/worker.c @@ -52,10 +52,6 @@ extern int chroot(const char *); #include "probe-table.h" #include "probe.h" -/* default max. memory usage ratio - used/total */ -/* can be overridden by environment variable OSCAP_PROBE_MEMORY_USAGE_RATIO */ -#define OSCAP_PROBE_MEMORY_USAGE_RATIO_DEFAULT 0.33 - extern bool OSCAP_GSYM(varref_handling); extern void *OSCAP_GSYM(probe_arg); @@ -1078,6 +1074,14 @@ SEXP_t *probe_worker(probe_t *probe, SEAP_msg_t *msg_in, int *ret) if (max_ratio > 0) pctx.max_mem_ratio = max_ratio; } + pctx.max_collected_items = OSCAP_PROBE_COLLECT_UNLIMITED; + char *max_collected_items_str = getenv("OSCAP_PROBE_MAX_COLLECTED_ITEMS"); + if (max_collected_items_str != NULL) { + int max_collected_items = strtol(max_collected_items_str, NULL, 0); + if (max_collected_items > 0) { + pctx.max_collected_items = max_collected_items; + } + } /* simple object */ pctx.icache = probe->icache; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a614a7073e..d4bfc7dcbe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory("mitre") add_subdirectory("nist") add_subdirectory("oscap_string") add_subdirectory("oval_details") +add_subdirectory("probe_behavior") add_subdirectory("probes") add_subdirectory("report") add_subdirectory("sce") diff --git a/tests/probe_behavior/CMakeLists.txt b/tests/probe_behavior/CMakeLists.txt new file mode 100644 index 0000000000..6329c1599e --- /dev/null +++ b/tests/probe_behavior/CMakeLists.txt @@ -0,0 +1 @@ +add_oscap_test("collect_limit.sh") diff --git a/tests/memory/cgroup_test.sh b/tests/probe_behavior/cgroup_test.sh similarity index 100% rename from tests/memory/cgroup_test.sh rename to tests/probe_behavior/cgroup_test.sh diff --git a/tests/probe_behavior/collect_limit.oval.xml b/tests/probe_behavior/collect_limit.oval.xml new file mode 100644 index 0000000000..63e4b4b535 --- /dev/null +++ b/tests/probe_behavior/collect_limit.oval.xml @@ -0,0 +1,37 @@ + + + + 5.10.1 + 0001-01-01T00:00:00+00:00 + + + + + + x + x + + x + + + + + + + + + + + + + + + + + /tmp/longfile + ^.*$ + 1 + + + + diff --git a/tests/probe_behavior/collect_limit.sh b/tests/probe_behavior/collect_limit.sh new file mode 100755 index 0000000000..d3c43b8aee --- /dev/null +++ b/tests/probe_behavior/collect_limit.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e -o pipefail + +. $builddir/tests/test_common.sh + +export OSCAP_PROBE_MAX_COLLECTED_ITEMS=100 +seq 110 > /tmp/longfile +result=$(mktemp) +$OSCAP oval eval --results "$result" $srcdir/collect_limit.oval.xml +assert_exists 1 '/oval_results/results/system/oval_system_characteristics/collected_objects/object' +assert_exists 1 '/oval_results/results/system/oval_system_characteristics/collected_objects/object[@flag="incomplete"]' +assert_exists 1 '/oval_results/results/system/oval_system_characteristics/collected_objects/object/message[@level="warning"]' +text="Object is incomplete because the object matches more than 100 items." +assert_exists 1 "/oval_results/results/system/oval_system_characteristics/collected_objects/object/message[text()=\"$text\"]" +assert_exists 100 '/oval_results/results/system/oval_system_characteristics/system_data/ind-sys:textfilecontent_item' +rm -f /tmp/longfile +rm -f "$result" diff --git a/tests/memory/ssg-rhel7-ds.xml.bz2 b/tests/probe_behavior/ssg-rhel7-ds.xml.bz2 similarity index 100% rename from tests/memory/ssg-rhel7-ds.xml.bz2 rename to tests/probe_behavior/ssg-rhel7-ds.xml.bz2 diff --git a/tests/memory/ssg-rhel7-oom-tailoring.xml b/tests/probe_behavior/ssg-rhel7-oom-tailoring.xml similarity index 100% rename from tests/memory/ssg-rhel7-oom-tailoring.xml rename to tests/probe_behavior/ssg-rhel7-oom-tailoring.xml diff --git a/tests/memory/ssg-rhel8-ds.xml.bz2 b/tests/probe_behavior/ssg-rhel8-ds.xml.bz2 similarity index 100% rename from tests/memory/ssg-rhel8-ds.xml.bz2 rename to tests/probe_behavior/ssg-rhel8-ds.xml.bz2 diff --git a/tests/memory/ssg-rhel8-oom-tailoring.xml b/tests/probe_behavior/ssg-rhel8-oom-tailoring.xml similarity index 100% rename from tests/memory/ssg-rhel8-oom-tailoring.xml rename to tests/probe_behavior/ssg-rhel8-oom-tailoring.xml