mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-09-28 13:00:23 +00:00
unbound: patch CVE-2026-50045
Details: https://nvd.nist.gov/vuln/detail/cve-2026-50045 Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
From ceadb55a62fb7f503b13e238043a68573890db97 Mon Sep 17 00:00:00 2001
|
||||
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
|
||||
Date: Wed, 22 Jul 2026 10:13:14 +0200
|
||||
Subject: [PATCH] - Fix CVE-2026-50045, 'max-global-quota' reset by DNSSEC
|
||||
validation restarts. Thanks to Kunjie Shang, University of Science and
|
||||
Technology of China, for the report.
|
||||
|
||||
(cherry picked from commit 364ac737f713b2a606f0fddecddb675d72a6a991)
|
||||
|
||||
CVE: CVE-2026-50045
|
||||
Upstream-Status: Backport [https://github.com/NLnetLabs/unbound/commit/364ac737f713b2a606f0fddecddb675d72a6a991]
|
||||
|
||||
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
|
||||
---
|
||||
iterator/iterator.c | 67 ++++++++++++++++++++++++++++++-------------
|
||||
util/module.h | 6 ++++
|
||||
validator/validator.c | 13 +++++++++
|
||||
3 files changed, 66 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/iterator/iterator.c b/iterator/iterator.c
|
||||
index 71e64655f..16dbc19de 100644
|
||||
--- a/iterator/iterator.c
|
||||
+++ b/iterator/iterator.c
|
||||
@@ -81,7 +81,8 @@ int BLACKLIST_PENALTY = (120000*4);
|
||||
/** Timeout when only a single probe query per IP is allowed. */
|
||||
int PROBE_MAXRTO = PROBE_MAXRTO_DEFAULT; /* in msec */
|
||||
|
||||
-static void target_count_increase_nx(struct iter_qstate* iq, int num);
|
||||
+static void target_count_increase_nx(struct module_qstate* qstate,
|
||||
+ struct iter_qstate* iq, int num);
|
||||
|
||||
int
|
||||
iter_init(struct module_env* env, int id)
|
||||
@@ -250,7 +251,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
|
||||
(dpns->got6 == 2 || !ie->supports_ipv6)) {
|
||||
dpns->resolved = 1; /* mark as failed */
|
||||
- target_count_increase_nx(super_iq, 1);
|
||||
+ target_count_increase_nx(super, super_iq, 1);
|
||||
}
|
||||
}
|
||||
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
|
||||
@@ -733,7 +734,7 @@ is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
|
||||
* created for the parent query.
|
||||
*/
|
||||
static void
|
||||
-target_count_create(struct iter_qstate* iq)
|
||||
+target_count_create(struct module_qstate* qstate, struct iter_qstate* iq)
|
||||
{
|
||||
if(!iq->target_count) {
|
||||
iq->target_count = (int*)calloc(TARGET_COUNT_MAX, sizeof(int));
|
||||
@@ -741,33 +742,57 @@ target_count_create(struct iter_qstate* iq)
|
||||
if(iq->target_count) {
|
||||
iq->target_count[TARGET_COUNT_REF] = 1;
|
||||
iq->nxns_dp = (uint8_t**)calloc(1, sizeof(uint8_t*));
|
||||
+ /* continue global quota from where it was. */
|
||||
+ if(qstate->global_quota_reached >
|
||||
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA])
|
||||
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] =
|
||||
+ qstate->global_quota_reached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-target_count_increase(struct iter_qstate* iq, int num)
|
||||
+target_count_store(struct module_qstate* qstate, struct iter_qstate* iq)
|
||||
{
|
||||
- target_count_create(iq);
|
||||
+ if(iq->target_count) {
|
||||
+ /* By storing the global quota counter, it stays
|
||||
+ * there to be picked up if the module is restarted,
|
||||
+ * eg. due to a validator retry, and then the
|
||||
+ * target_count_create routine picks it up. */
|
||||
+ if(iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] >
|
||||
+ qstate->global_quota_reached)
|
||||
+ qstate->global_quota_reached =
|
||||
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA];
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+target_count_increase(struct module_qstate* qstate,
|
||||
+ struct iter_qstate* iq, int num)
|
||||
+{
|
||||
+ target_count_create(qstate, iq);
|
||||
if(iq->target_count)
|
||||
iq->target_count[TARGET_COUNT_QUERIES] += num;
|
||||
iq->dp_target_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
-target_count_increase_nx(struct iter_qstate* iq, int num)
|
||||
+target_count_increase_nx(struct module_qstate* qstate,
|
||||
+ struct iter_qstate* iq, int num)
|
||||
{
|
||||
- target_count_create(iq);
|
||||
+ target_count_create(qstate, iq);
|
||||
if(iq->target_count)
|
||||
iq->target_count[TARGET_COUNT_NX] += num;
|
||||
}
|
||||
|
||||
static void
|
||||
-target_count_increase_global_quota(struct iter_qstate* iq, int num)
|
||||
+target_count_increase_global_quota(struct module_qstate* qstate,
|
||||
+ struct iter_qstate* iq, int num)
|
||||
{
|
||||
- target_count_create(iq);
|
||||
+ target_count_create(qstate, iq);
|
||||
if(iq->target_count)
|
||||
iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] += num;
|
||||
+ target_count_store(qstate, iq);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -860,7 +885,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
subiq = (struct iter_qstate*)subq->minfo[id];
|
||||
memset(subiq, 0, sizeof(*subiq));
|
||||
subiq->num_target_queries = 0;
|
||||
- target_count_create(iq);
|
||||
+ target_count_create(qstate, iq);
|
||||
subiq->target_count = iq->target_count;
|
||||
if(iq->target_count) {
|
||||
iq->target_count[TARGET_COUNT_REF] ++; /* extra reference */
|
||||
@@ -2233,7 +2258,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->num_target_queries += qs;
|
||||
- target_count_increase(iq, qs);
|
||||
+ target_count_increase(qstate, iq, qs);
|
||||
if(qs != 0) {
|
||||
qstate->ext_state[id] = module_wait_subquery;
|
||||
return 0; /* and wait for them */
|
||||
@@ -2289,7 +2314,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* lookups at a time. */
|
||||
verbose(VERB_ALGO, "try parent-side glue lookup");
|
||||
iq->num_target_queries += query_count;
|
||||
- target_count_increase(iq, query_count);
|
||||
+ target_count_increase(qstate, iq, query_count);
|
||||
qstate->ext_state[id] = module_wait_subquery;
|
||||
return 0;
|
||||
}
|
||||
@@ -2309,7 +2334,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
if(query_count != 0) { /* suspend to await results */
|
||||
verbose(VERB_ALGO, "try parent-side glue lookup");
|
||||
iq->num_target_queries += query_count;
|
||||
- target_count_increase(iq, query_count);
|
||||
+ target_count_increase(qstate, iq, query_count);
|
||||
qstate->ext_state[id] = module_wait_subquery;
|
||||
return 0;
|
||||
}
|
||||
@@ -2789,7 +2814,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->num_target_queries += extra;
|
||||
- target_count_increase(iq, extra);
|
||||
+ target_count_increase(qstate, iq, extra);
|
||||
if(iq->num_target_queries > 0) {
|
||||
/* wait to get all targets, we want to try em */
|
||||
verbose(VERB_ALGO, "wait for all targets for fallback");
|
||||
@@ -2840,7 +2865,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* errors ignored, these targets are not strictly necessary for
|
||||
* this result, we do not have to reply with SERVFAIL */
|
||||
iq->num_target_queries += extra;
|
||||
- target_count_increase(iq, extra);
|
||||
+ target_count_increase(qstate, iq, extra);
|
||||
}
|
||||
|
||||
/* Add the current set of unused targets to our queue. */
|
||||
@@ -2963,7 +2988,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->num_target_queries += qs;
|
||||
- target_count_increase(iq, qs);
|
||||
+ target_count_increase(qstate, iq, qs);
|
||||
}
|
||||
/* Since a target query might have been made, we
|
||||
* need to check again. */
|
||||
@@ -3023,7 +3048,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* this result, we do not have to reply with SERVFAIL */
|
||||
if(extra > 0) {
|
||||
iq->num_target_queries += extra;
|
||||
- target_count_increase(iq, extra);
|
||||
+ target_count_increase(qstate, iq, extra);
|
||||
check_waiting_queries(iq, qstate, id);
|
||||
/* undo qname minimise step because we'll get back here
|
||||
* to do it again */
|
||||
@@ -3036,7 +3061,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
}
|
||||
|
||||
- target_count_increase_global_quota(iq, 1);
|
||||
+ target_count_increase_global_quota(qstate, iq, 1);
|
||||
if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]
|
||||
> MAX_GLOBAL_QUOTA) {
|
||||
char s[LDNS_MAX_DOMAINLEN];
|
||||
@@ -3880,7 +3905,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
|
||||
/* no new addresses, increase the nxns counter, like
|
||||
* this could be a list of wildcards with no new
|
||||
* addresses */
|
||||
- target_count_increase_nx(foriq, 1);
|
||||
+ target_count_increase_nx(qstate, foriq, 1);
|
||||
}
|
||||
verbose(VERB_ALGO, "added target response");
|
||||
delegpt_log(VERB_ALGO, foriq->dp);
|
||||
@@ -3892,7 +3917,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
|
||||
dpns->resolved = 1; /* fail the target */
|
||||
/* do not count cached answers */
|
||||
if(qstate->reply_origin && qstate->reply_origin->len != 0) {
|
||||
- target_count_increase_nx(foriq, 1);
|
||||
+ target_count_increase_nx(qstate, foriq, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4117,6 +4142,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
|
||||
iq->deleg_msg?iq->deleg_msg->rep:
|
||||
(iq->response?iq->response->rep:NULL));
|
||||
+ target_count_store(qstate, iq);
|
||||
if(!iq->response) {
|
||||
verbose(VERB_ALGO, "No response is set, servfail");
|
||||
errinf(qstate, "(no response found at query finish)");
|
||||
@@ -4532,6 +4558,7 @@ iter_clear(struct module_qstate* qstate, int id)
|
||||
iq = (struct iter_qstate*)qstate->minfo[id];
|
||||
if(iq) {
|
||||
outbound_list_clear(&iq->outlist);
|
||||
+ target_count_store(qstate, iq);
|
||||
if(iq->target_count && --iq->target_count[TARGET_COUNT_REF] == 0) {
|
||||
free(iq->target_count);
|
||||
if(*iq->nxns_dp) free(*iq->nxns_dp);
|
||||
diff --git a/util/module.h b/util/module.h
|
||||
index edce4a523..b13b8de2f 100644
|
||||
--- a/util/module.h
|
||||
+++ b/util/module.h
|
||||
@@ -712,6 +712,12 @@ struct module_qstate {
|
||||
|
||||
/** whether the reply should be dropped */
|
||||
int is_drop;
|
||||
+ /** the global quota that was reached, by one of the modules.
|
||||
+ * So that continued counting can go on from that point. */
|
||||
+ int global_quota_reached;
|
||||
+ /** the global quota that a query started with, it is a subquery,
|
||||
+ * so that calling mesh states can see the increase. */
|
||||
+ int global_quota_started;
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/validator/validator.c b/validator/validator.c
|
||||
index 68c4bf643..c9896e4fb 100644
|
||||
--- a/validator/validator.c
|
||||
+++ b/validator/validator.c
|
||||
@@ -517,6 +517,14 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
|
||||
/* add our blacklist to the query blacklist */
|
||||
sock_list_merge(&(*newq)->blacklist, (*newq)->region,
|
||||
vq->chain_blacklist);
|
||||
+ /* start its global quota counter where this one is. */
|
||||
+ if(qstate->global_quota_reached >
|
||||
+ (*newq)->global_quota_reached) {
|
||||
+ (*newq)->global_quota_started =
|
||||
+ qstate->global_quota_reached;
|
||||
+ (*newq)->global_quota_reached =
|
||||
+ qstate->global_quota_reached;
|
||||
+ }
|
||||
}
|
||||
qstate->ext_state[id] = module_wait_subquery;
|
||||
return 1;
|
||||
@@ -3476,6 +3484,11 @@ val_inform_super(struct module_qstate* qstate, int id,
|
||||
verbose(VERB_ALGO, "super: has no validator state");
|
||||
return;
|
||||
}
|
||||
+ /* Pick up the global quota limit from the subquery. */
|
||||
+ if(qstate->global_quota_reached > qstate->global_quota_started) {
|
||||
+ super->global_quota_reached += qstate->global_quota_reached -
|
||||
+ qstate->global_quota_started;
|
||||
+ }
|
||||
if(vq->wait_prime_ta) {
|
||||
vq->wait_prime_ta = 0;
|
||||
process_prime_response(super, vq, id, qstate->return_rcode,
|
||||
@@ -28,6 +28,7 @@ SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=master;t
|
||||
file://CVE-2026-42955.patch \
|
||||
file://CVE-2026-44621.patch \
|
||||
file://CVE-2026-44687.patch \
|
||||
file://CVE-2026-50045.patch \
|
||||
"
|
||||
|
||||
SRCREV = "f6269baa605d31859f28770e01a24e3677e5f82c"
|
||||
|
||||
Reference in New Issue
Block a user