From 2ec808dda152f1fb022b05473a87c1d1db0cb074 Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Sat, 4 May 2024 22:08:24 +0200 Subject: [PATCH 2/3] Detect infinite recursion when evaluating requirements See RM #554 Signed-off-by: Alina Lenk --- common/requirements.c | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/common/requirements.c b/common/requirements.c index da6846be34..cbb37e1263 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -5742,6 +5742,26 @@ enum fc_tristate tri_req_present(const struct civ_map *nmap, const struct req_context *other_context, const struct requirement *req) { + /* Recursion detection: Maintain a stack of requirements that are + * currently being checked, and abort if we see the same req twice. + * Note that we're only storing the reqs themselves; we're not making + * an exception for the same req being evaluated against multiple + * different contexts. */ + struct recur_node { + const struct recur_node *next; + const struct requirement *req; + }; + /* The current deepest level of recursion */ + static const struct recur_node *recur_head = nullptr; + /* Stack node representing this call to tri_req_present() */ + const struct recur_node recur_current = { + .next = recur_head, + .req = req, + }; + const struct recur_node *recur_i; + + enum fc_tristate result; + if (!context) { context = req_context_empty(); } @@ -5757,8 +5777,29 @@ enum fc_tristate tri_req_present(const struct civ_map *nmap, fc_assert_ret_val(req_definitions[req->source.kind].cb != NULL, TRI_NO); - return req_definitions[req->source.kind].cb(nmap, context, - other_context, req); + for (recur_i = recur_head; recur_i != nullptr; recur_i = recur_i->next) { + if (are_requirements_equal(req, recur_i->req)) { + /* Infinite recursion! Abort and return TRI_MAYBE. */ + struct astring astr; + + log_warn("Infinite recursion when evaluating requirement: %s", + req_to_fstring(req, &astr)); + astr_free(&astr); + + return TRI_MAYBE; + } + } + + /* push */ + recur_head = &recur_current; + + result = req_definitions[req->source.kind].cb(nmap, context, + other_context, req); + + /* pop */ + recur_head = recur_current.next; + + return result; } /**********************************************************************//** -- 2.34.1