diff --git a/backport-Fix-removal-of-duplicate-certs-during-verification.patch b/backport-Fix-removal-of-duplicate-certs-during-verification.patch new file mode 100644 index 0000000000000000000000000000000000000000..a6b09406587a86de76f8fba61bec470411cb92e1 --- /dev/null +++ b/backport-Fix-removal-of-duplicate-certs-during-verification.patch @@ -0,0 +1,387 @@ +From e89378d5853d9bd0136b95aade37e23762ad9290 Mon Sep 17 00:00:00 2001 +From: Zoltan Fridrich +Date: Mon, 17 Oct 2022 15:27:37 +0200 +Subject: [PATCH] Fix removal of duplicate certs during verification + +Co-authored-by: Daiki Ueno +Signed-off-by: Zoltan Fridrich + +Reference: https://gitlab.com/gnutls/gnutls/-/commit/e89378d5853d9bd0136b95aade37e23762ad9290 +Conflict: .gitignore + +--- + lib/x509/verify-high.c | 101 ++++--------------- + tests/Makefile.am | 2 +- + tests/x509-verify-duplicate.c | 181 ++++++++++++++++++++++++++++++++++ + 3 files changed, 202 insertions(+), 82 deletions(-) + create mode 100644 tests/x509-verify-duplicate.c + +diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c +index 5698d4f..2c070b0 100644 +--- a/lib/x509/verify-high.c ++++ b/lib/x509/verify-high.c +@@ -35,6 +35,8 @@ + #include + #include "verify-high.h" + #include "intprops.h" ++#include "gl_linkedhash_list.h" ++#include "gl_list.h" + + struct named_cert_st { + gnutls_x509_crt_t cert; +@@ -68,82 +70,19 @@ struct gnutls_x509_trust_list_iter { + + #define DEFAULT_SIZE 127 + +-struct cert_set_node_st { +- gnutls_x509_crt_t *certs; +- unsigned int size; +-}; +- +-struct cert_set_st { +- struct cert_set_node_st *node; +- unsigned int size; +-}; +- +-static int +-cert_set_init(struct cert_set_st *set, unsigned int size) +-{ +- memset(set, 0, sizeof(*set)); +- +- set->size = size; +- set->node = gnutls_calloc(size, sizeof(*set->node)); +- if (!set->node) { +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); +- } +- +- return 0; +-} +- +-static void +-cert_set_deinit(struct cert_set_st *set) +-{ +- size_t i; +- +- for (i = 0; i < set->size; i++) { +- gnutls_free(set->node[i].certs); +- } +- +- gnutls_free(set->node); +-} +- + static bool +-cert_set_contains(struct cert_set_st *set, const gnutls_x509_crt_t cert) ++cert_eq(const void *cert1, const void *cert2) + { +- size_t hash, i; +- +- hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); +- hash %= set->size; +- +- for (i = 0; i < set->node[hash].size; i++) { +- if (unlikely(gnutls_x509_crt_equals(set->node[hash].certs[i], cert))) { +- return true; +- } +- } +- +- return false; ++ const gnutls_x509_crt_t c1 = (const gnutls_x509_crt_t)cert1; ++ const gnutls_x509_crt_t c2 = (const gnutls_x509_crt_t)cert2; ++ return gnutls_x509_crt_equals(c1, c2); + } + +-static int +-cert_set_add(struct cert_set_st *set, const gnutls_x509_crt_t cert) ++static size_t ++cert_hashcode(const void *cert) + { +- size_t hash; +- +- hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); +- hash %= set->size; +- +- if (unlikely(INT_ADD_OVERFLOW(set->node[hash].size, 1))) { +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); +- } +- +- set->node[hash].certs = +- _gnutls_reallocarray_fast(set->node[hash].certs, +- set->node[hash].size + 1, +- sizeof(*set->node[hash].certs)); +- if (!set->node[hash].certs) { +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); +- } +- set->node[hash].certs[set->node[hash].size] = cert; +- set->node[hash].size++; +- +- return 0; ++ const gnutls_x509_crt_t c = (const gnutls_x509_crt_t)cert; ++ return hash_pjw_bare(c->raw_dn.data, c->raw_dn.size) % DEFAULT_MAX_VERIFY_DEPTH; + } + + /** +@@ -1426,7 +1365,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + unsigned have_set_name = 0; + unsigned saved_output; + gnutls_datum_t ip = {NULL, 0}; +- struct cert_set_st cert_set = { NULL, 0 }; ++ gl_list_t records; + + if (cert_list == NULL || cert_list_size < 1) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); +@@ -1475,10 +1414,9 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t)); + cert_list = sorted; + +- ret = cert_set_init(&cert_set, DEFAULT_MAX_VERIFY_DEPTH); +- if (ret < 0) { +- return ret; +- } ++ records = gl_list_nx_create_empty(GL_LINKEDHASH_LIST, cert_eq, cert_hashcode, NULL, false); ++ if (records == NULL) ++ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i = 0; i < cert_list_size && + cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) { +@@ -1493,8 +1431,8 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + + /* Remove duplicates. Start with index 1, as the first element + * may be re-checked after issuer retrieval. */ +- for (j = 1; j < sorted_size; j++) { +- if (cert_set_contains(&cert_set, cert_list[i + j])) { ++ for (j = 0; j < sorted_size; j++) { ++ if (gl_list_search(records, cert_list[i + j])) { + if (i + j < cert_list_size - 1) { + memmove(&cert_list[i + j], + &cert_list[i + j + 1], +@@ -1511,8 +1449,8 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + + /* Record the certificates seen. */ + for (j = 0; j < sorted_size; j++, i++) { +- ret = cert_set_add(&cert_set, cert_list[i]); +- if (ret < 0) { ++ if (!gl_list_nx_add_last(records, cert_list[i])) { ++ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + } +@@ -1559,6 +1497,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + + /* Start again from the end of the previous segment. */ + i--; ++ gl_list_remove(records, cert_list[i]); + } + } + +@@ -1718,7 +1657,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + for (i = 0; i < retrieved_size; i++) { + gnutls_x509_crt_deinit(retrieved[i]); + } +- cert_set_deinit(&cert_set); ++ gl_list_free(records); + return ret; + } + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index b65fb65..f92a130 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -172,7 +172,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei + crlverify mini-dtls-discard mini-record-failure openconnect-dtls12 \ + tls12-rehandshake-cert-2 custom-urls set_x509_key_mem set_x509_key_file \ + tls12-rehandshake-cert-auto tls12-rehandshake-set-prio \ +- mini-chain-unsorted x509-verify-with-crl mini-dtls-mtu privkey-verify-broken \ ++ mini-chain-unsorted x509-verify-duplicate x509-verify-with-crl mini-dtls-mtu privkey-verify-broken \ + mini-dtls-record-asym key-import-export priority-set priority-set2 \ + pubkey-import-export sign-is-secure spki spki-abstract rsa-rsa-pss \ + mini-dtls-fork dtls-pthread mini-key-material x509cert-invalid \ +diff --git a/tests/x509-verify-duplicate.c b/tests/x509-verify-duplicate.c +new file mode 100644 +index 0000000..f47a8b2 +--- /dev/null ++++ b/tests/x509-verify-duplicate.c +@@ -0,0 +1,181 @@ ++/* ++ * Copyright (C) 2022 Red Hat, Inc. ++ * ++ * Author: Zoltan Fridrich ++ * ++ * This file is part of GnuTLS. ++ * ++ * GnuTLS is free software: you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GnuTLS is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GnuTLS. If not, see . ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++ ++#include "utils.h" ++ ++#define CHECK(X)\ ++{\ ++ r = X;\ ++ if (r < 0)\ ++ fail("error in %d: %s\n", __LINE__, gnutls_strerror(r));\ ++}\ ++ ++static char cert_pem[] = ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFLzCCBBegAwIBAgISAycvItcPAZ5yClzMOYYcod4cMA0GCSqGSIb3DQEBCwUA\n" ++ "MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\n" ++ "EwJSMzAeFw0yMjA4MjMwNjMzMjlaFw0yMjExMjEwNjMzMjhaMBcxFTATBgNVBAMT\n" ++ "DHZvaWRwb2ludC5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANSt\n" ++ "AazUWttuU/swyEdt70bpod6knYDJavnFUwicpT4ZfPh84Y2ci9Ay9oTVR8LzVq+o\n" ++ "3FIGxXlBFhCtoGA5k5Soao/JB40+gsY+O8LgcNAdejU78m5W4e2qXq4eu/4tFUCw\n" ++ "GkcRmqitnc5Jy0bEM+wCZKa42Lx0+WAhNRd/70yWIbzXOrXDnLgGc221JeYJ4it0\n" ++ "ajYcf3AZuSHhL3qsTLLzuYorPqWmDy27psUiDDJOIjxVbBCRL+AY40TsQm7CZZhZ\n" ++ "8sCkZU7rIvuDv7nf3QpUsF9Zqk9B3F4tTg0vsVuYeL1XCHGwpVeUS83MsZiLP8Zj\n" ++ "XGQTM6GiWuOAZ9JJjrsCAwEAAaOCAlgwggJUMA4GA1UdDwEB/wQEAwIFoDAdBgNV\n" ++ "HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E\n" ++ "FgQUlw1h3ZwSMKRwkrQ+F4XT3QV/tn8wHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA\n" ++ "5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMu\n" ++ "by5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8w\n" ++ "JwYDVR0RBCAwHoIOKi52b2lkcG9pbnQuaW+CDHZvaWRwb2ludC5pbzBMBgNVHSAE\n" ++ "RTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRw\n" ++ "Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB2\n" ++ "AN+lXqtogk8fbK3uuF9OPlrqzaISpGpejjsSwCBEXCpzAAABgsme4hAAAAQDAEcw\n" ++ "RQIhAP6sPHv1PJez/VRMw5xmAAkNU/q9ydq1mTgp7j5uBB9AAiAxm+teG9utZCLP\n" ++ "TTTv89FHwFV9omfZzDNAiNgg8METHwB3ACl5vvCeOTkh8FZzn2Old+W+V32cYAr4\n" ++ "+U1dJlwlXceEAAABgsme4gUAAAQDAEgwRgIhAPKWJ7WeuBUSnDqabTAVLKU+PpzA\n" ++ "bJJ9sehaCKW9AicZAiEAqphpC0lF4/iz2Gkxgd/DEkl9SyyAmR/lEJ7cWDMFhz8w\n" ++ "DQYJKoZIhvcNAQELBQADggEBAC0aCscObAdTerzGUrDsuQR5FuCTAmvdk3Isqjw1\n" ++ "dG3WuiwW1Z4ecpqCdvDSIv3toQDWVk6g/oa3fHDnY0/tu//vCwdneDdjK3gCM6cj\n" ++ "/q0cwj+rGFx/bEVz8PR5kc3DOHGKkmHPN1BNxeLBVpk4jxziXryAVbIvxq9JrGTE\n" ++ "SfWbWcMkHHw/QzpUfyD3B/GI8qw6XhdaNNkLDEDNV0sCPCuZYc5FBZzU4ExB2vMG\n" ++ "QVnPfxzKWmxHs10uxXyRZJlOrrbTGU8gi0vnOQZK290dtLzEyU2sdkic1ZSn+fCo\n" ++ "k++37mNDkiTnIQa3olRqHkypWqGfj8OyqU4XBV2Mmu4UATc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFLzCCBBegAwIBAgISAycvItcPAZ5yClzMOYYcod4cMA0GCSqGSIb3DQEBCwUA\n" ++ "MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\n" ++ "EwJSMzAeFw0yMjA4MjMwNjMzMjlaFw0yMjExMjEwNjMzMjhaMBcxFTATBgNVBAMT\n" ++ "DHZvaWRwb2ludC5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANSt\n" ++ "AazUWttuU/swyEdt70bpod6knYDJavnFUwicpT4ZfPh84Y2ci9Ay9oTVR8LzVq+o\n" ++ "3FIGxXlBFhCtoGA5k5Soao/JB40+gsY+O8LgcNAdejU78m5W4e2qXq4eu/4tFUCw\n" ++ "GkcRmqitnc5Jy0bEM+wCZKa42Lx0+WAhNRd/70yWIbzXOrXDnLgGc221JeYJ4it0\n" ++ "ajYcf3AZuSHhL3qsTLLzuYorPqWmDy27psUiDDJOIjxVbBCRL+AY40TsQm7CZZhZ\n" ++ "8sCkZU7rIvuDv7nf3QpUsF9Zqk9B3F4tTg0vsVuYeL1XCHGwpVeUS83MsZiLP8Zj\n" ++ "XGQTM6GiWuOAZ9JJjrsCAwEAAaOCAlgwggJUMA4GA1UdDwEB/wQEAwIFoDAdBgNV\n" ++ "HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E\n" ++ "FgQUlw1h3ZwSMKRwkrQ+F4XT3QV/tn8wHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA\n" ++ "5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMu\n" ++ "by5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8w\n" ++ "JwYDVR0RBCAwHoIOKi52b2lkcG9pbnQuaW+CDHZvaWRwb2ludC5pbzBMBgNVHSAE\n" ++ "RTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRw\n" ++ "Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB2\n" ++ "AN+lXqtogk8fbK3uuF9OPlrqzaISpGpejjsSwCBEXCpzAAABgsme4hAAAAQDAEcw\n" ++ "RQIhAP6sPHv1PJez/VRMw5xmAAkNU/q9ydq1mTgp7j5uBB9AAiAxm+teG9utZCLP\n" ++ "TTTv89FHwFV9omfZzDNAiNgg8METHwB3ACl5vvCeOTkh8FZzn2Old+W+V32cYAr4\n" ++ "+U1dJlwlXceEAAABgsme4gUAAAQDAEgwRgIhAPKWJ7WeuBUSnDqabTAVLKU+PpzA\n" ++ "bJJ9sehaCKW9AicZAiEAqphpC0lF4/iz2Gkxgd/DEkl9SyyAmR/lEJ7cWDMFhz8w\n" ++ "DQYJKoZIhvcNAQELBQADggEBAC0aCscObAdTerzGUrDsuQR5FuCTAmvdk3Isqjw1\n" ++ "dG3WuiwW1Z4ecpqCdvDSIv3toQDWVk6g/oa3fHDnY0/tu//vCwdneDdjK3gCM6cj\n" ++ "/q0cwj+rGFx/bEVz8PR5kc3DOHGKkmHPN1BNxeLBVpk4jxziXryAVbIvxq9JrGTE\n" ++ "SfWbWcMkHHw/QzpUfyD3B/GI8qw6XhdaNNkLDEDNV0sCPCuZYc5FBZzU4ExB2vMG\n" ++ "QVnPfxzKWmxHs10uxXyRZJlOrrbTGU8gi0vnOQZK290dtLzEyU2sdkic1ZSn+fCo\n" ++ "k++37mNDkiTnIQa3olRqHkypWqGfj8OyqU4XBV2Mmu4UATc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" ++ "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" ++ "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" ++ "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" ++ "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" ++ "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" ++ "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" ++ "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" ++ "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" ++ "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" ++ "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" ++ "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" ++ "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" ++ "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" ++ "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" ++ "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" ++ "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" ++ "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" ++ "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" ++ "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" ++ "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" ++ "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" ++ "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" ++ "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" ++ "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" ++ "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" ++ "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" ++ "nLRbwHOoq7hHwg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/\n" ++ "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" ++ "DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow\n" ++ "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" ++ "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB\n" ++ "AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC\n" ++ "ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL\n" ++ "wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D\n" ++ "LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK\n" ++ "4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5\n" ++ "bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y\n" ++ "sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ\n" ++ "Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4\n" ++ "FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc\n" ++ "SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql\n" ++ "PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND\n" ++ "TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw\n" ++ "SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1\n" ++ "c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx\n" ++ "+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB\n" ++ "ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu\n" ++ "b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E\n" ++ "U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu\n" ++ "MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC\n" ++ "5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW\n" ++ "9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG\n" ++ "WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O\n" ++ "he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC\n" ++ "Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n" ++ "-----END CERTIFICATE-----\n"; ++ ++void doit(void) ++{ ++ int r; ++ unsigned i, certs_size, out; ++ unsigned flags = GNUTLS_VERIFY_DO_NOT_ALLOW_SAME | GNUTLS_VERIFY_DISABLE_TIME_CHECKS; ++ gnutls_x509_trust_list_t tl; ++ gnutls_x509_crt_t *certs = NULL; ++ gnutls_datum_t cert = { (unsigned char *)cert_pem, sizeof(cert_pem) - 1 }; ++ ++ CHECK(gnutls_x509_crt_list_import2(&certs, &certs_size, &cert, GNUTLS_X509_FMT_PEM, 0)); ++ CHECK(gnutls_x509_trust_list_init(&tl, 0)); ++ CHECK(gnutls_x509_trust_list_add_cas(tl, certs + certs_size - 1, 1, 0)); ++ CHECK(gnutls_x509_trust_list_verify_crt(tl, certs, certs_size, flags, &out, NULL)); ++ ++ if (out) ++ fail("Not verified\n"); ++ ++ gnutls_x509_trust_list_deinit(tl, 0); ++ for (i = 0; i < certs_size; ++i) ++ gnutls_x509_crt_deinit(certs[i]); ++ gnutls_free(certs); ++} +-- +2.33.0 + diff --git a/backport-add-gnulib-files.patch b/backport-add-gnulib-files.patch new file mode 100644 index 0000000000000000000000000000000000000000..fbe6c5ae9ec1724bf0c15cca30f813bcdc631d83 --- /dev/null +++ b/backport-add-gnulib-files.patch @@ -0,0 +1,2766 @@ +From e40367c0b1f4f0b32ef7aef56d0c3f8c39bacec9 Mon Sep 17 00:00:00 2001 +From: xuraoqing +Date: Tue, 9 Apr 2024 15:47:38 +0800 +Subject: [PATCH] add gnulib files + +Reference: https://gitlab.com/libidn/gnulib-mirror/-/tree/master/lib +Conflict: NA + +files(*.c,*.h) below from gnulib, but no commit id in gnutls repository. +fix CVE-2024-28835 need those files. + +Makefile.am and Makefile.in generated by automake. + +--- + gl/Makefile.am | 13 + + gl/Makefile.in | 61 +- + gl/gl_anyhash1.h | 31 + + gl/gl_anyhash2.h | 82 +++ + gl/gl_anyhash_primes.h | 87 +++ + gl/gl_anylinked_list1.h | 48 ++ + gl/gl_anylinked_list2.h | 1215 +++++++++++++++++++++++++++++++++++++++ + gl/gl_linkedhash_list.c | 114 ++++ + gl/gl_linkedhash_list.h | 34 ++ + gl/gl_list.c | 3 + + gl/gl_list.h | 914 +++++++++++++++++++++++++++++ + 11 files changed, 2578 insertions(+), 24 deletions(-) + create mode 100644 gl/gl_anyhash1.h + create mode 100644 gl/gl_anyhash2.h + create mode 100644 gl/gl_anyhash_primes.h + create mode 100644 gl/gl_anylinked_list1.h + create mode 100644 gl/gl_anylinked_list2.h + create mode 100644 gl/gl_linkedhash_list.c + create mode 100644 gl/gl_linkedhash_list.h + create mode 100644 gl/gl_list.c + create mode 100644 gl/gl_list.h + +diff --git a/gl/Makefile.am b/gl/Makefile.am +index 89a5c6a..31e62b2 100644 +--- a/gl/Makefile.am ++++ b/gl/Makefile.am +@@ -59,6 +59,7 @@ + # ldd \ + # lib-msvc-compat \ + # lib-symbol-versions \ ++# linkedhash-list \ + # maintainer-makefile \ + # manywarnings \ + # memmem-simple \ +@@ -663,6 +664,18 @@ EXTRA_DIST += limits.in.h + + ## end gnulib module limits-h + ++## begin gnulib module linkedhash-list ++ ++libgnu_la_SOURCES += gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash1.h gl_anyhash2.h gl_anyhash_primes.h gl_anylinked_list1.h gl_anylinked_list2.h ++ ++## end gnulib module linkedhash-list ++ ++## begin gnulib module list ++ ++libgnu_la_SOURCES += gl_list.h gl_list.c ++ ++## end gnulib module list ++ + ## begin gnulib module lseek + + +diff --git a/gl/Makefile.in b/gl/Makefile.in +index b1ece83..f57ecfb 100644 +--- a/gl/Makefile.in ++++ b/gl/Makefile.in +@@ -73,6 +73,7 @@ + # ldd \ + # lib-msvc-compat \ + # lib-symbol-versions \ ++# linkedhash-list \ + # maintainer-makefile \ + # manywarnings \ + # memmem-simple \ +@@ -358,8 +359,9 @@ am__DEPENDENCIES_1 = + am__dirstamp = $(am__leading_dot)dirstamp + am_libgnu_la_OBJECTS = bitrotate.lo c-ctype.lo c-strcasecmp.lo \ + c-strncasecmp.lo cloexec.lo fd-hook.lo hash.lo \ +- hash-pjw-bare.lo malloca.lo read-file.lo stat-time.lo \ +- sys_socket.lo glthread/threadlib.lo unistd.lo xsize.lo ++ hash-pjw-bare.lo gl_linkedhash_list.lo gl_list.lo malloca.lo \ ++ read-file.lo stat-time.lo sys_socket.lo glthread/threadlib.lo \ ++ unistd.lo xsize.lo + libgnu_la_OBJECTS = $(am_libgnu_la_OBJECTS) + AM_V_lt = $(am__v_lt_@AM_V@) + am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +@@ -394,26 +396,28 @@ am__depfiles_remade = ./$(DEPDIR)/alloca.Plo ./$(DEPDIR)/asnprintf.Plo \ + ./$(DEPDIR)/fstat.Plo ./$(DEPDIR)/ftell.Plo \ + ./$(DEPDIR)/ftello.Plo ./$(DEPDIR)/getdelim.Plo \ + ./$(DEPDIR)/getdtablesize.Plo ./$(DEPDIR)/getline.Plo \ +- ./$(DEPDIR)/gettimeofday.Plo ./$(DEPDIR)/hash-pjw-bare.Plo \ +- ./$(DEPDIR)/hash.Plo ./$(DEPDIR)/inet_ntop.Plo \ +- ./$(DEPDIR)/inet_pton.Plo ./$(DEPDIR)/itold.Plo \ +- ./$(DEPDIR)/lseek.Plo ./$(DEPDIR)/malloc.Plo \ +- ./$(DEPDIR)/malloca.Plo ./$(DEPDIR)/memchr.Plo \ +- ./$(DEPDIR)/memmem.Plo ./$(DEPDIR)/msvc-inval.Plo \ +- ./$(DEPDIR)/msvc-nothrow.Plo ./$(DEPDIR)/open.Plo \ +- ./$(DEPDIR)/printf-args.Plo ./$(DEPDIR)/printf-parse.Plo \ +- ./$(DEPDIR)/read-file.Plo ./$(DEPDIR)/realloc.Plo \ +- ./$(DEPDIR)/secure_getenv.Plo ./$(DEPDIR)/setsockopt.Plo \ +- ./$(DEPDIR)/snprintf.Plo ./$(DEPDIR)/stat-time.Plo \ +- ./$(DEPDIR)/stat-w32.Plo ./$(DEPDIR)/stat.Plo \ +- ./$(DEPDIR)/stpcpy.Plo ./$(DEPDIR)/strcasecmp.Plo \ +- ./$(DEPDIR)/strdup.Plo ./$(DEPDIR)/strncasecmp.Plo \ +- ./$(DEPDIR)/strndup.Plo ./$(DEPDIR)/strnlen.Plo \ +- ./$(DEPDIR)/strtok_r.Plo ./$(DEPDIR)/strverscmp.Plo \ +- ./$(DEPDIR)/sys_socket.Plo ./$(DEPDIR)/time_r.Plo \ +- ./$(DEPDIR)/unistd.Plo ./$(DEPDIR)/vasnprintf.Plo \ +- ./$(DEPDIR)/vasprintf.Plo ./$(DEPDIR)/vsnprintf.Plo \ +- ./$(DEPDIR)/xsize.Plo glthread/$(DEPDIR)/threadlib.Plo ++ ./$(DEPDIR)/gettimeofday.Plo \ ++ ./$(DEPDIR)/gl_linkedhash_list.Plo ./$(DEPDIR)/gl_list.Plo \ ++ ./$(DEPDIR)/hash-pjw-bare.Plo ./$(DEPDIR)/hash.Plo \ ++ ./$(DEPDIR)/inet_ntop.Plo ./$(DEPDIR)/inet_pton.Plo \ ++ ./$(DEPDIR)/itold.Plo ./$(DEPDIR)/lseek.Plo \ ++ ./$(DEPDIR)/malloc.Plo ./$(DEPDIR)/malloca.Plo \ ++ ./$(DEPDIR)/memchr.Plo ./$(DEPDIR)/memmem.Plo \ ++ ./$(DEPDIR)/msvc-inval.Plo ./$(DEPDIR)/msvc-nothrow.Plo \ ++ ./$(DEPDIR)/open.Plo ./$(DEPDIR)/printf-args.Plo \ ++ ./$(DEPDIR)/printf-parse.Plo ./$(DEPDIR)/read-file.Plo \ ++ ./$(DEPDIR)/realloc.Plo ./$(DEPDIR)/secure_getenv.Plo \ ++ ./$(DEPDIR)/setsockopt.Plo ./$(DEPDIR)/snprintf.Plo \ ++ ./$(DEPDIR)/stat-time.Plo ./$(DEPDIR)/stat-w32.Plo \ ++ ./$(DEPDIR)/stat.Plo ./$(DEPDIR)/stpcpy.Plo \ ++ ./$(DEPDIR)/strcasecmp.Plo ./$(DEPDIR)/strdup.Plo \ ++ ./$(DEPDIR)/strncasecmp.Plo ./$(DEPDIR)/strndup.Plo \ ++ ./$(DEPDIR)/strnlen.Plo ./$(DEPDIR)/strtok_r.Plo \ ++ ./$(DEPDIR)/strverscmp.Plo ./$(DEPDIR)/sys_socket.Plo \ ++ ./$(DEPDIR)/time_r.Plo ./$(DEPDIR)/unistd.Plo \ ++ ./$(DEPDIR)/vasnprintf.Plo ./$(DEPDIR)/vasprintf.Plo \ ++ ./$(DEPDIR)/vsnprintf.Plo ./$(DEPDIR)/xsize.Plo \ ++ glthread/$(DEPDIR)/threadlib.Plo + am__mv = mv -f + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +@@ -2275,8 +2279,11 @@ AM_CFLAGS = + libgnu_la_SOURCES = bitrotate.h bitrotate.c c-ctype.h c-ctype.c \ + c-strcase.h c-strcasecmp.c c-strncasecmp.c cloexec.c fd-hook.c \ + gettext.h hash.c hash-pjw-bare.h hash-pjw-bare.c idx.h \ +- malloca.c minmax.h read-file.c size_max.h stat-time.c \ +- sys_socket.c glthread/threadlib.c unistd.c xsize.h xsize.c ++ gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash1.h \ ++ gl_anyhash2.h gl_anyhash_primes.h gl_anylinked_list1.h \ ++ gl_anylinked_list2.h gl_list.h gl_list.c malloca.c minmax.h \ ++ read-file.c size_max.h stat-time.c sys_socket.c \ ++ glthread/threadlib.c unistd.c xsize.h xsize.c + libgnu_la_LIBADD = $(gl_LTLIBOBJS) @LTALLOCA@ + libgnu_la_DEPENDENCIES = $(gl_LTLIBOBJS) @LTALLOCA@ + EXTRA_libgnu_la_SOURCES = alloca.c close.c dup2.c explicit_bzero.c \ +@@ -2407,6 +2414,8 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getdtablesize.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getline.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gettimeofday.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_linkedhash_list.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_list.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-pjw-bare.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet_ntop.Plo@am__quote@ # am--include-marker +@@ -2714,6 +2723,8 @@ distclean: distclean-recursive + -rm -f ./$(DEPDIR)/getdtablesize.Plo + -rm -f ./$(DEPDIR)/getline.Plo + -rm -f ./$(DEPDIR)/gettimeofday.Plo ++ -rm -f ./$(DEPDIR)/gl_linkedhash_list.Plo ++ -rm -f ./$(DEPDIR)/gl_list.Plo + -rm -f ./$(DEPDIR)/hash-pjw-bare.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/inet_ntop.Plo +@@ -2821,6 +2832,8 @@ maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/getdtablesize.Plo + -rm -f ./$(DEPDIR)/getline.Plo + -rm -f ./$(DEPDIR)/gettimeofday.Plo ++ -rm -f ./$(DEPDIR)/gl_linkedhash_list.Plo ++ -rm -f ./$(DEPDIR)/gl_list.Plo + -rm -f ./$(DEPDIR)/hash-pjw-bare.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/inet_ntop.Plo +diff --git a/gl/gl_anyhash1.h b/gl/gl_anyhash1.h +new file mode 100644 +index 0000000..3253673 +--- /dev/null ++++ b/gl/gl_anyhash1.h +@@ -0,0 +1,31 @@ ++/* Hash table for sequential list, set, and map data type. ++ Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of ++ gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, ++ gl_linkedhash_set.c, gl_hash_set.c, ++ gl_linkedhash_map.c, gl_hash_map.c. */ ++ ++/* Hash table entry. */ ++struct gl_hash_entry ++{ ++ struct gl_hash_entry *hash_next; /* chain of entries in same bucket */ ++ size_t hashcode; /* cache of the hash code of ++ - the key (for map data type) or ++ - the value (for list, set data types) */ ++}; ++typedef struct gl_hash_entry * gl_hash_entry_t; +diff --git a/gl/gl_anyhash2.h b/gl/gl_anyhash2.h +new file mode 100644 +index 0000000..d8b5fb0 +--- /dev/null ++++ b/gl/gl_anyhash2.h +@@ -0,0 +1,82 @@ ++/* Hash table for sequential list, set, and map data type. ++ Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of ++ gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, ++ gl_linkedhash_set.c, gl_hash_set.c, ++ gl_linkedhash_map.c, gl_hash_map.c. */ ++ ++#include "gl_anyhash_primes.h" ++ ++/* Resizes the hash table with a new estimated size. */ ++static void ++hash_resize (CONTAINER_T container, size_t estimate) ++{ ++ size_t new_size = next_prime (estimate); ++ ++ if (new_size > container->table_size) ++ { ++ gl_hash_entry_t *old_table = container->table; ++ /* Allocate the new table. */ ++ gl_hash_entry_t *new_table; ++ size_t i; ++ ++ if (size_overflow_p (xtimes (new_size, sizeof (gl_hash_entry_t)))) ++ goto fail; ++ new_table = ++ (gl_hash_entry_t *) calloc (new_size, sizeof (gl_hash_entry_t)); ++ if (new_table == NULL) ++ goto fail; ++ ++ /* Iterate through the entries of the old table. */ ++ for (i = container->table_size; i > 0; ) ++ { ++ gl_hash_entry_t node = old_table[--i]; ++ ++ while (node != NULL) ++ { ++ gl_hash_entry_t next = node->hash_next; ++ /* Add the entry to the new table. */ ++ size_t bucket = node->hashcode % new_size; ++ node->hash_next = new_table[bucket]; ++ new_table[bucket] = node; ++ ++ node = next; ++ } ++ } ++ ++ container->table = new_table; ++ container->table_size = new_size; ++ free (old_table); ++ } ++ return; ++ ++ fail: ++ /* Just continue without resizing the table. */ ++ return; ++} ++ ++/* Resizes the hash table if needed, after CONTAINER_COUNT (container) was ++ incremented. */ ++static void ++hash_resize_after_add (CONTAINER_T container) ++{ ++ size_t count = CONTAINER_COUNT (container); ++ size_t estimate = xsum (count, count / 2); /* 1.5 * count */ ++ if (estimate > container->table_size) ++ hash_resize (container, estimate); ++} +diff --git a/gl/gl_anyhash_primes.h b/gl/gl_anyhash_primes.h +new file mode 100644 +index 0000000..b723e64 +--- /dev/null ++++ b/gl/gl_anyhash_primes.h +@@ -0,0 +1,87 @@ ++/* Table of primes, for use by hash tables. ++ Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Array of primes, approximately in steps of factor 1.2. ++ This table was computed by executing the Common Lisp expression ++ (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i)))) ++ and feeding the result to PARI/gp. */ ++static const size_t primes[] = ++ { ++ 11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199, ++ 239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543, ++ 3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899, ++ 22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849, ++ 140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887, ++ 723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277, ++ 3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307, ++ 13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233, ++ 47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469, ++ 171731387, 206077643, 247293161, 296751781, 356102141, 427322587, ++ 512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331, ++ 1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL, ++ 3810050851UL, ++#if SIZE_MAX > 4294967295UL ++ 4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL, ++ 11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL, ++ 28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL, ++ 70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL, ++ 146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL, ++ 302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL, ++ 628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL, ++ 1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL, ++ 2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL, ++ 5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL, ++ 11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL, ++ 24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL, ++ 49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL, ++ 103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL, ++ 214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL, ++ 445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL, ++ 923114351670013UL, 1107737222003791UL, 1329284666404567UL, ++ 1595141599685509UL, 1914169919622551UL, 2297003903547091UL, ++ 2756404684256459UL, 3307685621107757UL, 3969222745329323UL, ++ 4763067294395177UL, 5715680753274209UL, 6858816903929113UL, ++ 8230580284714831UL, 9876696341657791UL, 11852035609989371UL, ++ 14222442731987227UL, 17066931278384657UL, 20480317534061597UL, ++ 24576381040873903UL, 29491657249048679UL, 35389988698858471UL, ++ 42467986438630267UL, 50961583726356109UL, 61153900471627387UL, ++ 73384680565952851UL, 88061616679143347UL, 105673940014972061UL, ++ 126808728017966413UL, 152170473621559703UL, 182604568345871671UL, ++ 219125482015045997UL, 262950578418055169UL, 315540694101666193UL, ++ 378648832921999397UL, 454378599506399233UL, 545254319407679131UL, ++ 654305183289214771UL, 785166219947057701UL, 942199463936469157UL, ++ 1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL, ++ 1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL, ++ 3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL, ++ 5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL, ++ 10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL, ++ 17419784962119465179UL, ++#endif ++ SIZE_MAX /* sentinel, to ensure the search terminates */ ++ }; ++ ++/* Returns a suitable prime >= ESTIMATE. */ ++static size_t ++next_prime (size_t estimate) ++{ ++ size_t i; ++ ++ for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++) ++ if (primes[i] >= estimate) ++ return primes[i]; ++ return SIZE_MAX; /* not a prime, but better than nothing */ ++} +diff --git a/gl/gl_anylinked_list1.h b/gl/gl_anylinked_list1.h +new file mode 100644 +index 0000000..caaaa4a +--- /dev/null ++++ b/gl/gl_anylinked_list1.h +@@ -0,0 +1,48 @@ ++/* Sequential list data type implemented by a linked list. ++ Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of gl_linked_list.c and gl_linkedhash_list.c. */ ++ ++/* -------------------------- gl_list_t Data Type -------------------------- */ ++ ++/* Concrete list node implementation, valid for this file only. */ ++struct gl_list_node_impl ++{ ++#if WITH_HASHTABLE ++ struct gl_hash_entry h; /* hash table entry fields; must be first */ ++#endif ++ struct gl_list_node_impl *next; ++ struct gl_list_node_impl *prev; ++ const void *value; ++}; ++ ++/* Concrete gl_list_impl type, valid for this file only. */ ++struct gl_list_impl ++{ ++ struct gl_list_impl_base base; ++#if WITH_HASHTABLE ++ /* A hash table: managed as an array of collision lists. */ ++ struct gl_hash_entry **table; ++ size_t table_size; ++#endif ++ /* A circular list anchored at root. ++ The first node is = root.next, the last node is = root.prev. ++ The root's value is unused. */ ++ struct gl_list_node_impl root; ++ /* Number of list nodes, excluding the root. */ ++ size_t count; ++}; +diff --git a/gl/gl_anylinked_list2.h b/gl/gl_anylinked_list2.h +new file mode 100644 +index 0000000..6af3f76 +--- /dev/null ++++ b/gl/gl_anylinked_list2.h +@@ -0,0 +1,1215 @@ ++/* Sequential list data type implemented by a linked list. ++ Copyright (C) 2006-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of gl_linked_list.c and gl_linkedhash_list.c. */ ++ ++/* If the symbol SIGNAL_SAFE_LIST is defined, the code is compiled in such ++ a way that a gl_list_t data structure may be used from within a signal ++ handler. The operations allowed in the signal handler are: ++ gl_list_iterator, gl_list_iterator_next, gl_list_iterator_free. ++ The list and node fields that are therefore accessed from the signal handler ++ are: ++ list->root, node->next, node->value. ++ We are careful to make modifications to these fields only in an order ++ that maintains the consistency of the list data structure at any moment, ++ and we use 'volatile' assignments to prevent the compiler from reordering ++ such assignments. */ ++#ifdef SIGNAL_SAFE_LIST ++# define ASYNCSAFE(type) *(type volatile *)& ++#else ++# define ASYNCSAFE(type) ++#endif ++ ++/* -------------------------- gl_list_t Data Type -------------------------- */ ++ ++static gl_list_t ++gl_linked_nx_create_empty (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates) ++{ ++ struct gl_list_impl *list = ++ (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl)); ++ ++ if (list == NULL) ++ return NULL; ++ ++ list->base.vtable = implementation; ++ list->base.equals_fn = equals_fn; ++ list->base.hashcode_fn = hashcode_fn; ++ list->base.dispose_fn = dispose_fn; ++ list->base.allow_duplicates = allow_duplicates; ++#if WITH_HASHTABLE ++ list->table_size = 11; ++ list->table = ++ (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t)); ++ if (list->table == NULL) ++ goto fail; ++#endif ++ list->root.next = &list->root; ++ list->root.prev = &list->root; ++ list->count = 0; ++ ++ return list; ++ ++#if WITH_HASHTABLE ++ fail: ++ free (list); ++ return NULL; ++#endif ++} ++ ++static gl_list_t ++gl_linked_nx_create (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates, ++ size_t count, const void **contents) ++{ ++ struct gl_list_impl *list = ++ (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl)); ++ gl_list_node_t tail; ++ ++ if (list == NULL) ++ return NULL; ++ ++ list->base.vtable = implementation; ++ list->base.equals_fn = equals_fn; ++ list->base.hashcode_fn = hashcode_fn; ++ list->base.dispose_fn = dispose_fn; ++ list->base.allow_duplicates = allow_duplicates; ++#if WITH_HASHTABLE ++ { ++ size_t estimate = xsum (count, count / 2); /* 1.5 * count */ ++ if (estimate < 10) ++ estimate = 10; ++ list->table_size = next_prime (estimate); ++ if (size_overflow_p (xtimes (list->table_size, sizeof (gl_hash_entry_t)))) ++ goto fail1; ++ list->table = ++ (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t)); ++ if (list->table == NULL) ++ goto fail1; ++ } ++#endif ++ list->count = count; ++ tail = &list->root; ++ for (; count > 0; contents++, count--) ++ { ++ gl_list_node_t node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (node == NULL) ++ goto fail2; ++ ++ node->value = *contents; ++#if WITH_HASHTABLE ++ node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (node->value) ++ : (size_t)(uintptr_t) node->value); ++ ++ /* Add node to the hash table. */ ++ if (add_to_bucket (list, node) < 0) ++ { ++ free (node); ++ goto fail2; ++ } ++#endif ++ ++ /* Add node to the list. */ ++ node->prev = tail; ++ tail->next = node; ++ tail = node; ++ } ++ tail->next = &list->root; ++ list->root.prev = tail; ++ ++ return list; ++ ++ fail2: ++ { ++ gl_list_node_t node; ++ ++ for (node = tail; node != &list->root; ) ++ { ++ gl_list_node_t prev = node->prev; ++ ++ free (node); ++ node = prev; ++ } ++ } ++#if WITH_HASHTABLE ++ free (list->table); ++ fail1: ++#endif ++ free (list); ++ return NULL; ++} ++ ++static size_t _GL_ATTRIBUTE_PURE ++gl_linked_size (gl_list_t list) ++{ ++ return list->count; ++} ++ ++static const void * _GL_ATTRIBUTE_PURE ++gl_linked_node_value (gl_list_t list _GL_ATTRIBUTE_MAYBE_UNUSED, ++ gl_list_node_t node) ++{ ++ return node->value; ++} ++ ++static int ++gl_linked_node_nx_set_value (gl_list_t list _GL_ATTRIBUTE_MAYBE_UNUSED, ++ gl_list_node_t node, ++ const void *elt) ++{ ++#if WITH_HASHTABLE ++ if (elt != node->value) ++ { ++ size_t new_hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ ++ if (new_hashcode != node->h.hashcode) ++ { ++ remove_from_bucket (list, node); ++ node->value = elt; ++ node->h.hashcode = new_hashcode; ++ if (add_to_bucket (list, node) < 0) ++ { ++ /* Out of memory. We removed node from a bucket but cannot add ++ it to another bucket. In order to avoid inconsistencies, we ++ must remove node entirely from the list. */ ++ gl_list_node_t before_removed = node->prev; ++ gl_list_node_t after_removed = node->next; ++ ASYNCSAFE(gl_list_node_t) before_removed->next = after_removed; ++ after_removed->prev = before_removed; ++ list->count--; ++ free (node); ++ return -1; ++ } ++ } ++ else ++ node->value = elt; ++ } ++#else ++ node->value = elt; ++#endif ++ return 0; ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_next_node (gl_list_t list, gl_list_node_t node) ++{ ++ return (node->next != &list->root ? node->next : NULL); ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_previous_node (gl_list_t list, gl_list_node_t node) ++{ ++ return (node->prev != &list->root ? node->prev : NULL); ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_first_node (gl_list_t list) ++{ ++ if (list->count > 0) ++ return list->root.next; ++ else ++ return NULL; ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_last_node (gl_list_t list) ++{ ++ if (list->count > 0) ++ return list->root.prev; ++ else ++ return NULL; ++} ++ ++static const void * _GL_ATTRIBUTE_PURE ++gl_linked_get_at (gl_list_t list, size_t position) ++{ ++ size_t count = list->count; ++ gl_list_node_t node; ++ ++ if (!(position < count)) ++ /* Invalid argument. */ ++ abort (); ++ /* Here we know count > 0. */ ++ if (position <= ((count - 1) / 2)) ++ { ++ node = list->root.next; ++ for (; position > 0; position--) ++ node = node->next; ++ } ++ else ++ { ++ position = count - 1 - position; ++ node = list->root.prev; ++ for (; position > 0; position--) ++ node = node->prev; ++ } ++ return node->value; ++} ++ ++static gl_list_node_t ++gl_linked_nx_set_at (gl_list_t list, size_t position, const void *elt) ++{ ++ size_t count = list->count; ++ gl_list_node_t node; ++ ++ if (!(position < count)) ++ /* Invalid argument. */ ++ abort (); ++ /* Here we know count > 0. */ ++ if (position <= ((count - 1) / 2)) ++ { ++ node = list->root.next; ++ for (; position > 0; position--) ++ node = node->next; ++ } ++ else ++ { ++ position = count - 1 - position; ++ node = list->root.prev; ++ for (; position > 0; position--) ++ node = node->prev; ++ } ++#if WITH_HASHTABLE ++ if (elt != node->value) ++ { ++ size_t new_hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ ++ if (new_hashcode != node->h.hashcode) ++ { ++ remove_from_bucket (list, node); ++ node->value = elt; ++ node->h.hashcode = new_hashcode; ++ if (add_to_bucket (list, node) < 0) ++ { ++ /* Out of memory. We removed node from a bucket but cannot add ++ it to another bucket. In order to avoid inconsistencies, we ++ must remove node entirely from the list. */ ++ gl_list_node_t before_removed = node->prev; ++ gl_list_node_t after_removed = node->next; ++ ASYNCSAFE(gl_list_node_t) before_removed->next = after_removed; ++ after_removed->prev = before_removed; ++ list->count--; ++ free (node); ++ return NULL; ++ } ++ } ++ else ++ node->value = elt; ++ } ++#else ++ node->value = elt; ++#endif ++ return node; ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_search_from_to (gl_list_t list, size_t start_index, size_t end_index, ++ const void *elt) ++{ ++ size_t count = list->count; ++ ++ if (!(start_index <= end_index && end_index <= count)) ++ /* Invalid arguments. */ ++ abort (); ++ { ++#if WITH_HASHTABLE ++ size_t hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ size_t bucket = hashcode % list->table_size; ++ gl_listelement_equals_fn equals = list->base.equals_fn; ++ ++ if (!list->base.allow_duplicates) ++ { ++ /* Look for the first match in the hash bucket. */ ++ gl_list_node_t found = NULL; ++ gl_list_node_t node; ++ ++ for (node = (gl_list_node_t) list->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ { ++ found = node; ++ break; ++ } ++ if (start_index > 0) ++ /* Look whether found's index is < start_index. */ ++ for (node = list->root.next; ; node = node->next) ++ { ++ if (node == found) ++ return NULL; ++ if (--start_index == 0) ++ break; ++ } ++ if (end_index < count) ++ /* Look whether found's index is >= end_index. */ ++ { ++ end_index = count - end_index; ++ for (node = list->root.prev; ; node = node->prev) ++ { ++ if (node == found) ++ return NULL; ++ if (--end_index == 0) ++ break; ++ } ++ } ++ return found; ++ } ++ else ++ { ++ /* Look whether there is more than one match in the hash bucket. */ ++ bool multiple_matches = false; ++ gl_list_node_t first_match = NULL; ++ gl_list_node_t node; ++ ++ for (node = (gl_list_node_t) list->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ { ++ if (first_match == NULL) ++ first_match = node; ++ else ++ { ++ multiple_matches = true; ++ break; ++ } ++ } ++ if (multiple_matches) ++ { ++ /* We need the match with the smallest index. But we don't have ++ a fast mapping node -> index. So we have to walk the list. */ ++ end_index -= start_index; ++ node = list->root.next; ++ for (; start_index > 0; start_index--) ++ node = node->next; ++ ++ for (; ++ end_index > 0; ++ node = node->next, end_index--) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ return node; ++ /* The matches must have all been at indices < start_index or ++ >= end_index. */ ++ return NULL; ++ } ++ else ++ { ++ if (start_index > 0) ++ /* Look whether first_match's index is < start_index. */ ++ for (node = list->root.next; node != &list->root; node = node->next) ++ { ++ if (node == first_match) ++ return NULL; ++ if (--start_index == 0) ++ break; ++ } ++ if (end_index < list->count) ++ /* Look whether first_match's index is >= end_index. */ ++ { ++ end_index = list->count - end_index; ++ for (node = list->root.prev; ; node = node->prev) ++ { ++ if (node == first_match) ++ return NULL; ++ if (--end_index == 0) ++ break; ++ } ++ } ++ return first_match; ++ } ++ } ++#else ++ gl_listelement_equals_fn equals = list->base.equals_fn; ++ gl_list_node_t node = list->root.next; ++ ++ end_index -= start_index; ++ for (; start_index > 0; start_index--) ++ node = node->next; ++ ++ if (equals != NULL) ++ { ++ for (; end_index > 0; node = node->next, end_index--) ++ if (equals (elt, node->value)) ++ return node; ++ } ++ else ++ { ++ for (; end_index > 0; node = node->next, end_index--) ++ if (elt == node->value) ++ return node; ++ } ++ return NULL; ++#endif ++ } ++} ++ ++static size_t _GL_ATTRIBUTE_PURE ++gl_linked_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, ++ const void *elt) ++{ ++ size_t count = list->count; ++ ++ if (!(start_index <= end_index && end_index <= count)) ++ /* Invalid arguments. */ ++ abort (); ++ { ++#if WITH_HASHTABLE ++ /* Here the hash table doesn't help much. It only allows us to minimize ++ the number of equals() calls, by looking up first the node and then ++ its index. */ ++ size_t hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ size_t bucket = hashcode % list->table_size; ++ gl_listelement_equals_fn equals = list->base.equals_fn; ++ gl_list_node_t node; ++ ++ /* First step: Look up the node. */ ++ if (!list->base.allow_duplicates) ++ { ++ /* Look for the first match in the hash bucket. */ ++ for (node = (gl_list_node_t) list->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ break; ++ } ++ else ++ { ++ /* Look whether there is more than one match in the hash bucket. */ ++ bool multiple_matches = false; ++ gl_list_node_t first_match = NULL; ++ ++ for (node = (gl_list_node_t) list->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ { ++ if (first_match == NULL) ++ first_match = node; ++ else ++ { ++ multiple_matches = true; ++ break; ++ } ++ } ++ if (multiple_matches) ++ { ++ /* We need the match with the smallest index. But we don't have ++ a fast mapping node -> index. So we have to walk the list. */ ++ size_t index; ++ ++ index = start_index; ++ node = list->root.next; ++ for (; start_index > 0; start_index--) ++ node = node->next; ++ ++ for (; ++ index < end_index; ++ node = node->next, index++) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ return index; ++ /* The matches must have all been at indices < start_index or ++ >= end_index. */ ++ return (size_t)(-1); ++ } ++ node = first_match; ++ } ++ ++ /* Second step: Look up the index of the node. */ ++ if (node == NULL) ++ return (size_t)(-1); ++ else ++ { ++ size_t index = 0; ++ ++ for (; node->prev != &list->root; node = node->prev) ++ index++; ++ ++ if (index >= start_index && index < end_index) ++ return index; ++ else ++ return (size_t)(-1); ++ } ++#else ++ gl_listelement_equals_fn equals = list->base.equals_fn; ++ size_t index = start_index; ++ gl_list_node_t node = list->root.next; ++ ++ for (; start_index > 0; start_index--) ++ node = node->next; ++ ++ if (equals != NULL) ++ { ++ for (; ++ index < end_index; ++ node = node->next, index++) ++ if (equals (elt, node->value)) ++ return index; ++ } ++ else ++ { ++ for (; ++ index < end_index; ++ node = node->next, index++) ++ if (elt == node->value) ++ return index; ++ } ++ return (size_t)(-1); ++#endif ++ } ++} ++ ++static gl_list_node_t ++gl_linked_nx_add_first (gl_list_t list, const void *elt) ++{ ++ gl_list_node_t node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (node == NULL) ++ return NULL; ++ ++ ASYNCSAFE(const void *) node->value = elt; ++#if WITH_HASHTABLE ++ node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (node->value) ++ : (size_t)(uintptr_t) node->value); ++ ++ /* Add node to the hash table. */ ++ if (add_to_bucket (list, node) < 0) ++ { ++ free (node); ++ return NULL; ++ } ++#endif ++ ++ /* Add node to the list. */ ++ node->prev = &list->root; ++ ASYNCSAFE(gl_list_node_t) node->next = list->root.next; ++ node->next->prev = node; ++ ASYNCSAFE(gl_list_node_t) list->root.next = node; ++ list->count++; ++ ++#if WITH_HASHTABLE ++ hash_resize_after_add (list); ++#endif ++ ++ return node; ++} ++ ++static gl_list_node_t ++gl_linked_nx_add_last (gl_list_t list, const void *elt) ++{ ++ gl_list_node_t node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (node == NULL) ++ return NULL; ++ ++ ASYNCSAFE(const void *) node->value = elt; ++#if WITH_HASHTABLE ++ node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (node->value) ++ : (size_t)(uintptr_t) node->value); ++ ++ /* Add node to the hash table. */ ++ if (add_to_bucket (list, node) < 0) ++ { ++ free (node); ++ return NULL; ++ } ++#endif ++ ++ /* Add node to the list. */ ++ ASYNCSAFE(gl_list_node_t) node->next = &list->root; ++ node->prev = list->root.prev; ++ ASYNCSAFE(gl_list_node_t) node->prev->next = node; ++ list->root.prev = node; ++ list->count++; ++ ++#if WITH_HASHTABLE ++ hash_resize_after_add (list); ++#endif ++ ++ return node; ++} ++ ++static gl_list_node_t ++gl_linked_nx_add_before (gl_list_t list, gl_list_node_t node, const void *elt) ++{ ++ gl_list_node_t new_node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (new_node == NULL) ++ return NULL; ++ ++ ASYNCSAFE(const void *) new_node->value = elt; ++#if WITH_HASHTABLE ++ new_node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (new_node->value) ++ : (size_t)(uintptr_t) new_node->value); ++ ++ /* Add new_node to the hash table. */ ++ if (add_to_bucket (list, new_node) < 0) ++ { ++ free (new_node); ++ return NULL; ++ } ++#endif ++ ++ /* Add new_node to the list. */ ++ ASYNCSAFE(gl_list_node_t) new_node->next = node; ++ new_node->prev = node->prev; ++ ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; ++ node->prev = new_node; ++ list->count++; ++ ++#if WITH_HASHTABLE ++ hash_resize_after_add (list); ++#endif ++ ++ return new_node; ++} ++ ++static gl_list_node_t ++gl_linked_nx_add_after (gl_list_t list, gl_list_node_t node, const void *elt) ++{ ++ gl_list_node_t new_node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (new_node == NULL) ++ return NULL; ++ ++ ASYNCSAFE(const void *) new_node->value = elt; ++#if WITH_HASHTABLE ++ new_node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (new_node->value) ++ : (size_t)(uintptr_t) new_node->value); ++ ++ /* Add new_node to the hash table. */ ++ if (add_to_bucket (list, new_node) < 0) ++ { ++ free (new_node); ++ return NULL; ++ } ++#endif ++ ++ /* Add new_node to the list. */ ++ new_node->prev = node; ++ ASYNCSAFE(gl_list_node_t) new_node->next = node->next; ++ new_node->next->prev = new_node; ++ ASYNCSAFE(gl_list_node_t) node->next = new_node; ++ list->count++; ++ ++#if WITH_HASHTABLE ++ hash_resize_after_add (list); ++#endif ++ ++ return new_node; ++} ++ ++static gl_list_node_t ++gl_linked_nx_add_at (gl_list_t list, size_t position, const void *elt) ++{ ++ size_t count = list->count; ++ gl_list_node_t new_node; ++ ++ if (!(position <= count)) ++ /* Invalid argument. */ ++ abort (); ++ ++ new_node = (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ if (new_node == NULL) ++ return NULL; ++ ++ ASYNCSAFE(const void *) new_node->value = elt; ++#if WITH_HASHTABLE ++ new_node->h.hashcode = ++ (list->base.hashcode_fn != NULL ++ ? list->base.hashcode_fn (new_node->value) ++ : (size_t)(uintptr_t) new_node->value); ++ ++ /* Add new_node to the hash table. */ ++ if (add_to_bucket (list, new_node) < 0) ++ { ++ free (new_node); ++ return NULL; ++ } ++#endif ++ ++ /* Add new_node to the list. */ ++ if (position <= (count / 2)) ++ { ++ gl_list_node_t node; ++ ++ node = &list->root; ++ for (; position > 0; position--) ++ node = node->next; ++ new_node->prev = node; ++ ASYNCSAFE(gl_list_node_t) new_node->next = node->next; ++ new_node->next->prev = new_node; ++ ASYNCSAFE(gl_list_node_t) node->next = new_node; ++ } ++ else ++ { ++ gl_list_node_t node; ++ ++ position = count - position; ++ node = &list->root; ++ for (; position > 0; position--) ++ node = node->prev; ++ ASYNCSAFE(gl_list_node_t) new_node->next = node; ++ new_node->prev = node->prev; ++ ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; ++ node->prev = new_node; ++ } ++ list->count++; ++ ++#if WITH_HASHTABLE ++ hash_resize_after_add (list); ++#endif ++ ++ return new_node; ++} ++ ++static bool ++gl_linked_remove_node (gl_list_t list, gl_list_node_t node) ++{ ++ gl_list_node_t prev; ++ gl_list_node_t next; ++ ++#if WITH_HASHTABLE ++ /* Remove node from the hash table. */ ++ remove_from_bucket (list, node); ++#endif ++ ++ /* Remove node from the list. */ ++ prev = node->prev; ++ next = node->next; ++ ++ ASYNCSAFE(gl_list_node_t) prev->next = next; ++ next->prev = prev; ++ list->count--; ++ ++ if (list->base.dispose_fn != NULL) ++ list->base.dispose_fn (node->value); ++ free (node); ++ return true; ++} ++ ++static bool ++gl_linked_remove_at (gl_list_t list, size_t position) ++{ ++ size_t count = list->count; ++ gl_list_node_t removed_node; ++ ++ if (!(position < count)) ++ /* Invalid argument. */ ++ abort (); ++ /* Here we know count > 0. */ ++ if (position <= ((count - 1) / 2)) ++ { ++ gl_list_node_t node; ++ gl_list_node_t after_removed; ++ ++ node = &list->root; ++ for (; position > 0; position--) ++ node = node->next; ++ removed_node = node->next; ++ after_removed = node->next->next; ++ ASYNCSAFE(gl_list_node_t) node->next = after_removed; ++ after_removed->prev = node; ++ } ++ else ++ { ++ gl_list_node_t node; ++ gl_list_node_t before_removed; ++ ++ position = count - 1 - position; ++ node = &list->root; ++ for (; position > 0; position--) ++ node = node->prev; ++ removed_node = node->prev; ++ before_removed = node->prev->prev; ++ node->prev = before_removed; ++ ASYNCSAFE(gl_list_node_t) before_removed->next = node; ++ } ++#if WITH_HASHTABLE ++ remove_from_bucket (list, removed_node); ++#endif ++ list->count--; ++ ++ if (list->base.dispose_fn != NULL) ++ list->base.dispose_fn (removed_node->value); ++ free (removed_node); ++ return true; ++} ++ ++static bool ++gl_linked_remove (gl_list_t list, const void *elt) ++{ ++ gl_list_node_t node = gl_linked_search_from_to (list, 0, list->count, elt); ++ ++ if (node != NULL) ++ return gl_linked_remove_node (list, node); ++ else ++ return false; ++} ++ ++static void ++gl_linked_list_free (gl_list_t list) ++{ ++ gl_listelement_dispose_fn dispose = list->base.dispose_fn; ++ gl_list_node_t node; ++ ++ for (node = list->root.next; node != &list->root; ) ++ { ++ gl_list_node_t next = node->next; ++ if (dispose != NULL) ++ dispose (node->value); ++ free (node); ++ node = next; ++ } ++#if WITH_HASHTABLE ++ free (list->table); ++#endif ++ free (list); ++} ++ ++/* --------------------- gl_list_iterator_t Data Type --------------------- */ ++ ++static gl_list_iterator_t _GL_ATTRIBUTE_PURE ++gl_linked_iterator (gl_list_t list) ++{ ++ gl_list_iterator_t result; ++ ++ result.vtable = list->base.vtable; ++ result.list = list; ++ result.p = list->root.next; ++ result.q = &list->root; ++#if defined GCC_LINT || defined lint ++ result.i = 0; ++ result.j = 0; ++ result.count = 0; ++#endif ++ ++ return result; ++} ++ ++static gl_list_iterator_t _GL_ATTRIBUTE_PURE ++gl_linked_iterator_from_to (gl_list_t list, ++ size_t start_index, size_t end_index) ++{ ++ gl_list_iterator_t result; ++ size_t n1, n2, n3; ++ ++ if (!(start_index <= end_index && end_index <= list->count)) ++ /* Invalid arguments. */ ++ abort (); ++ result.vtable = list->base.vtable; ++ result.list = list; ++ n1 = start_index; ++ n2 = end_index - start_index; ++ n3 = list->count - end_index; ++ /* Find the maximum among n1, n2, n3, so as to reduce the number of ++ loop iterations to n1 + n2 + n3 - max(n1,n2,n3). */ ++ if (n1 > n2 && n1 > n3) ++ { ++ /* n1 is the maximum, use n2 and n3. */ ++ gl_list_node_t node; ++ size_t i; ++ ++ node = &list->root; ++ for (i = n3; i > 0; i--) ++ node = node->prev; ++ result.q = node; ++ for (i = n2; i > 0; i--) ++ node = node->prev; ++ result.p = node; ++ } ++ else if (n2 > n3) ++ { ++ /* n2 is the maximum, use n1 and n3. */ ++ gl_list_node_t node; ++ size_t i; ++ ++ node = list->root.next; ++ for (i = n1; i > 0; i--) ++ node = node->next; ++ result.p = node; ++ ++ node = &list->root; ++ for (i = n3; i > 0; i--) ++ node = node->prev; ++ result.q = node; ++ } ++ else ++ { ++ /* n3 is the maximum, use n1 and n2. */ ++ gl_list_node_t node; ++ size_t i; ++ ++ node = list->root.next; ++ for (i = n1; i > 0; i--) ++ node = node->next; ++ result.p = node; ++ for (i = n2; i > 0; i--) ++ node = node->next; ++ result.q = node; ++ } ++ ++#if defined GCC_LINT || defined lint ++ result.i = 0; ++ result.j = 0; ++ result.count = 0; ++#endif ++ ++ return result; ++} ++ ++static bool ++gl_linked_iterator_next (gl_list_iterator_t *iterator, ++ const void **eltp, gl_list_node_t *nodep) ++{ ++ if (iterator->p != iterator->q) ++ { ++ gl_list_node_t node = (gl_list_node_t) iterator->p; ++ *eltp = node->value; ++ if (nodep != NULL) ++ *nodep = node; ++ iterator->p = node->next; ++ return true; ++ } ++ else ++ return false; ++} ++ ++static void ++gl_linked_iterator_free (gl_list_iterator_t *iterator _GL_ATTRIBUTE_MAYBE_UNUSED) ++{ ++} ++ ++/* ---------------------- Sorted gl_list_t Data Type ---------------------- */ ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, ++ const void *elt) ++{ ++ gl_list_node_t node; ++ ++ for (node = list->root.next; node != &list->root; node = node->next) ++ { ++ int cmp = compar (node->value, elt); ++ ++ if (cmp > 0) ++ break; ++ if (cmp == 0) ++ return node; ++ } ++ return NULL; ++} ++ ++static gl_list_node_t _GL_ATTRIBUTE_PURE ++gl_linked_sortedlist_search_from_to (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t low, size_t high, ++ const void *elt) ++{ ++ size_t count = list->count; ++ ++ if (!(low <= high && high <= list->count)) ++ /* Invalid arguments. */ ++ abort (); ++ ++ high -= low; ++ if (high > 0) ++ { ++ /* Here we know low < count. */ ++ size_t position = low; ++ gl_list_node_t node; ++ ++ if (position <= ((count - 1) / 2)) ++ { ++ node = list->root.next; ++ for (; position > 0; position--) ++ node = node->next; ++ } ++ else ++ { ++ position = count - 1 - position; ++ node = list->root.prev; ++ for (; position > 0; position--) ++ node = node->prev; ++ } ++ ++ do ++ { ++ int cmp = compar (node->value, elt); ++ ++ if (cmp > 0) ++ break; ++ if (cmp == 0) ++ return node; ++ node = node->next; ++ } ++ while (--high > 0); ++ } ++ return NULL; ++} ++ ++static size_t _GL_ATTRIBUTE_PURE ++gl_linked_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, ++ const void *elt) ++{ ++ gl_list_node_t node; ++ size_t index; ++ ++ for (node = list->root.next, index = 0; ++ node != &list->root; ++ node = node->next, index++) ++ { ++ int cmp = compar (node->value, elt); ++ ++ if (cmp > 0) ++ break; ++ if (cmp == 0) ++ return index; ++ } ++ return (size_t)(-1); ++} ++ ++static size_t _GL_ATTRIBUTE_PURE ++gl_linked_sortedlist_indexof_from_to (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t low, size_t high, ++ const void *elt) ++{ ++ size_t count = list->count; ++ ++ if (!(low <= high && high <= list->count)) ++ /* Invalid arguments. */ ++ abort (); ++ ++ high -= low; ++ if (high > 0) ++ { ++ /* Here we know low < count. */ ++ size_t index = low; ++ size_t position = low; ++ gl_list_node_t node; ++ ++ if (position <= ((count - 1) / 2)) ++ { ++ node = list->root.next; ++ for (; position > 0; position--) ++ node = node->next; ++ } ++ else ++ { ++ position = count - 1 - position; ++ node = list->root.prev; ++ for (; position > 0; position--) ++ node = node->prev; ++ } ++ ++ do ++ { ++ int cmp = compar (node->value, elt); ++ ++ if (cmp > 0) ++ break; ++ if (cmp == 0) ++ return index; ++ node = node->next; ++ index++; ++ } ++ while (--high > 0); ++ } ++ return (size_t)(-1); ++} ++ ++static gl_list_node_t ++gl_linked_sortedlist_nx_add (gl_list_t list, gl_listelement_compar_fn compar, ++ const void *elt) ++{ ++ gl_list_node_t node; ++ ++ for (node = list->root.next; node != &list->root; node = node->next) ++ if (compar (node->value, elt) >= 0) ++ return gl_linked_nx_add_before (list, node, elt); ++ return gl_linked_nx_add_last (list, elt); ++} ++ ++static bool ++gl_linked_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, ++ const void *elt) ++{ ++ gl_list_node_t node; ++ ++ for (node = list->root.next; node != &list->root; node = node->next) ++ { ++ int cmp = compar (node->value, elt); ++ ++ if (cmp > 0) ++ break; ++ if (cmp == 0) ++ return gl_linked_remove_node (list, node); ++ } ++ return false; ++} +diff --git a/gl/gl_linkedhash_list.c b/gl/gl_linkedhash_list.c +new file mode 100644 +index 0000000..70eca52 +--- /dev/null ++++ b/gl/gl_linkedhash_list.c +@@ -0,0 +1,114 @@ ++/* Sequential list data type implemented by a hash table with a linked list. ++ Copyright (C) 2006, 2008-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++/* Specification. */ ++#include "gl_linkedhash_list.h" ++ ++#include /* for uintptr_t, SIZE_MAX */ ++#include ++ ++#include "xsize.h" ++ ++#define WITH_HASHTABLE 1 ++ ++/* -------------------------- gl_list_t Data Type -------------------------- */ ++ ++/* Generic hash-table code. */ ++#include "gl_anyhash1.h" ++ ++/* Generic linked list code. */ ++#include "gl_anylinked_list1.h" ++ ++/* Generic hash-table code. */ ++#define CONTAINER_T gl_list_t ++#define CONTAINER_COUNT(list) (list)->count ++#include "gl_anyhash2.h" ++ ++/* Add a node to the hash table structure. */ ++static void ++add_to_bucket (gl_list_t list, gl_list_node_t node) ++{ ++ size_t bucket = node->h.hashcode % list->table_size; ++ ++ node->h.hash_next = list->table[bucket]; ++ list->table[bucket] = &node->h; ++} ++/* Tell all compilers that the return value is 0. */ ++#define add_to_bucket(list,node) ((add_to_bucket) (list, node), 0) ++ ++/* Remove a node from the hash table structure. */ ++static void ++remove_from_bucket (gl_list_t list, gl_list_node_t node) ++{ ++ size_t bucket = node->h.hashcode % list->table_size; ++ gl_hash_entry_t *p; ++ ++ for (p = &list->table[bucket]; ; p = &(*p)->hash_next) ++ { ++ if (*p == &node->h) ++ { ++ *p = node->h.hash_next; ++ break; ++ } ++ if (*p == NULL) ++ /* node is not in the right bucket. Did the hash codes ++ change inadvertently? */ ++ abort (); ++ } ++} ++ ++/* Generic linked list code. */ ++#include "gl_anylinked_list2.h" ++ ++ ++const struct gl_list_implementation gl_linkedhash_list_implementation = ++ { ++ gl_linked_nx_create_empty, ++ gl_linked_nx_create, ++ gl_linked_size, ++ gl_linked_node_value, ++ gl_linked_node_nx_set_value, ++ gl_linked_next_node, ++ gl_linked_previous_node, ++ gl_linked_first_node, ++ gl_linked_last_node, ++ gl_linked_get_at, ++ gl_linked_nx_set_at, ++ gl_linked_search_from_to, ++ gl_linked_indexof_from_to, ++ gl_linked_nx_add_first, ++ gl_linked_nx_add_last, ++ gl_linked_nx_add_before, ++ gl_linked_nx_add_after, ++ gl_linked_nx_add_at, ++ gl_linked_remove_node, ++ gl_linked_remove_at, ++ gl_linked_remove, ++ gl_linked_list_free, ++ gl_linked_iterator, ++ gl_linked_iterator_from_to, ++ gl_linked_iterator_next, ++ gl_linked_iterator_free, ++ gl_linked_sortedlist_search, ++ gl_linked_sortedlist_search_from_to, ++ gl_linked_sortedlist_indexof, ++ gl_linked_sortedlist_indexof_from_to, ++ gl_linked_sortedlist_nx_add, ++ gl_linked_sortedlist_remove ++ }; +diff --git a/gl/gl_linkedhash_list.h b/gl/gl_linkedhash_list.h +new file mode 100644 +index 0000000..f908588 +--- /dev/null ++++ b/gl/gl_linkedhash_list.h +@@ -0,0 +1,34 @@ ++/* Sequential list data type implemented by a hash table with a linked list. ++ Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_LINKEDHASH_LIST_H ++#define _GL_LINKEDHASH_LIST_H ++ ++#include "gl_list.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern const struct gl_list_implementation gl_linkedhash_list_implementation; ++#define GL_LINKEDHASH_LIST &gl_linkedhash_list_implementation ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _GL_LINKEDHASH_LIST_H */ +diff --git a/gl/gl_list.c b/gl/gl_list.c +new file mode 100644 +index 0000000..8793298 +--- /dev/null ++++ b/gl/gl_list.c +@@ -0,0 +1,3 @@ ++#include ++#define GL_LIST_INLINE _GL_EXTERN_INLINE ++#include "gl_list.h" +diff --git a/gl/gl_list.h b/gl/gl_list.h +new file mode 100644 +index 0000000..7fc22bb +--- /dev/null ++++ b/gl/gl_list.h +@@ -0,0 +1,914 @@ ++/* Abstract sequential list data type. -*- coding: utf-8 -*- ++ Copyright (C) 2006-2021 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_LIST_H ++#define _GL_LIST_H ++ ++#include ++#include ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++#ifndef GL_LIST_INLINE ++# define GL_LIST_INLINE _GL_INLINE ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* gl_list is an abstract list data type. It can contain any number of ++ objects ('void *' or 'const void *' pointers) in any given order. ++ Duplicates are allowed, but can optionally be forbidden. ++ ++ There are several implementations of this list datatype, optimized for ++ different operations or for memory. You can start using the simplest list ++ implementation, GL_ARRAY_LIST, and switch to a different implementation ++ later, when you realize which operations are performed the most frequently. ++ The API of the different implementations is exactly the same; when ++ switching to a different implementation, you only have to change the ++ gl_list_create call. ++ ++ The implementations are: ++ GL_ARRAY_LIST a growable array ++ GL_CARRAY_LIST a growable circular array ++ GL_LINKED_LIST a linked list ++ GL_AVLTREE_LIST a binary tree (AVL tree) ++ GL_RBTREE_LIST a binary tree (red-black tree) ++ GL_LINKEDHASH_LIST a hash table with a linked list ++ GL_AVLTREEHASH_LIST a hash table with a binary tree (AVL tree) ++ GL_RBTREEHASH_LIST a hash table with a binary tree (red-black tree) ++ ++ The memory consumption is asymptotically the same: O(1) for every object ++ in the list. When looking more closely at the average memory consumed ++ for an object, GL_ARRAY_LIST is the most compact representation, and ++ GL_LINKEDHASH_LIST and GL_TREEHASH_LIST need more memory. ++ ++ The guaranteed average performance of the operations is, for a list of ++ n elements: ++ ++ Operation ARRAY LINKED TREE LINKEDHASH TREEHASH ++ CARRAY with|without with|without ++ duplicates duplicates ++ ++ gl_list_size O(1) O(1) O(1) O(1) O(1) ++ gl_list_node_value O(1) O(1) O(1) O(1) O(1) ++ gl_list_node_set_value O(1) O(1) O(1) O(1) O((log n)²)/O(1) ++ gl_list_next_node O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_previous_node O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_first_node O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_last_node O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_get_at O(1) O(n) O(log n) O(n) O(log n) ++ gl_list_get_first O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_get_last O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_set_at O(1) O(n) O(log n) O(n) O((log n)²)/O(log n) ++ gl_list_set_first O(1) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_set_last O(1) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_search O(n) O(n) O(n) O(n)/O(1) O(log n)/O(1) ++ gl_list_search_from O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_search_from_to O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_indexof O(n) O(n) O(n) O(n) O(log n) ++ gl_list_indexof_from O(n) O(n) O(n) O(n) O((log n)²)/O(log n) ++ gl_list_indexof_from_to O(n) O(n) O(n) O(n) O((log n)²)/O(log n) ++ gl_list_add_first O(n)/O(1) O(1) O(log n) O(1) O((log n)²)/O(log n) ++ gl_list_add_last O(1) O(1) O(log n) O(1) O((log n)²)/O(log n) ++ gl_list_add_before O(n) O(1) O(log n) O(1) O((log n)²)/O(log n) ++ gl_list_add_after O(n) O(1) O(log n) O(1) O((log n)²)/O(log n) ++ gl_list_add_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) ++ gl_list_remove_node O(n) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_remove_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) ++ gl_list_remove_first O(n)/O(1) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_remove_last O(1) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_remove O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) ++ gl_list_iterator O(1) O(1) O(log n) O(1) O(log n) ++ gl_list_iterator_from_to O(1) O(n) O(log n) O(n) O(log n) ++ gl_list_iterator_next O(1) O(1) O(log n) O(1) O(log n) ++ gl_sortedlist_search O(log n) O(n) O(log n) O(n) O(log n) ++ gl_sortedlist_search_from O(log n) O(n) O(log n) O(n) O(log n) ++ gl_sortedlist_indexof O(log n) O(n) O(log n) O(n) O(log n) ++ gl_sortedlist_indexof_fro O(log n) O(n) O(log n) O(n) O(log n) ++ gl_sortedlist_add O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) ++ gl_sortedlist_remove O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) ++ */ ++ ++/* -------------------------- gl_list_t Data Type -------------------------- */ ++ ++/* Type of function used to compare two elements. ++ NULL denotes pointer comparison. */ ++typedef bool (*gl_listelement_equals_fn) (const void *elt1, const void *elt2); ++ ++/* Type of function used to compute a hash code. ++ NULL denotes a function that depends only on the pointer itself. */ ++typedef size_t (*gl_listelement_hashcode_fn) (const void *elt); ++ ++/* Type of function used to dispose an element once it's removed from a list. ++ NULL denotes a no-op. */ ++typedef void (*gl_listelement_dispose_fn) (const void *elt); ++ ++struct gl_list_impl; ++/* Type representing an entire list. */ ++typedef struct gl_list_impl * gl_list_t; ++ ++struct gl_list_node_impl; ++/* Type representing the position of an element in the list, in a way that ++ is more adapted to the list implementation than a plain index. ++ Note: It is invalidated by insertions and removals! */ ++typedef struct gl_list_node_impl * gl_list_node_t; ++ ++struct gl_list_implementation; ++/* Type representing a list datatype implementation. */ ++typedef const struct gl_list_implementation * gl_list_implementation_t; ++ ++#if 0 /* Unless otherwise specified, these are defined inline below. */ ++ ++/* Creates an empty list. ++ IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST, ++ GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST, ++ GL_RBTREEHASH_LIST. ++ EQUALS_FN is an element comparison function or NULL. ++ HASHCODE_FN is an element hash code function or NULL. ++ DISPOSE_FN is an element disposal function or NULL. ++ ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in ++ the list. The implementation may verify this at runtime. */ ++/* declared in gl_xlist.h */ ++extern gl_list_t gl_list_create_empty (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_t gl_list_nx_create_empty (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates); ++ ++/* Creates a list with given contents. ++ IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST, ++ GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST, ++ GL_RBTREEHASH_LIST. ++ EQUALS_FN is an element comparison function or NULL. ++ HASHCODE_FN is an element hash code function or NULL. ++ DISPOSE_FN is an element disposal function or NULL. ++ ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in ++ the list. The implementation may verify this at runtime. ++ COUNT is the number of initial elements. ++ CONTENTS[0..COUNT-1] is the initial contents. */ ++/* declared in gl_xlist.h */ ++extern gl_list_t gl_list_create (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates, ++ size_t count, const void **contents); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_t gl_list_nx_create (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates, ++ size_t count, const void **contents); ++ ++/* Returns the current number of elements in a list. */ ++extern size_t gl_list_size (gl_list_t list); ++ ++/* Returns the element value represented by a list node. */ ++extern const void * gl_list_node_value (gl_list_t list, gl_list_node_t node); ++ ++/* Replaces the element value represented by a list node. */ ++/* declared in gl_xlist.h */ ++extern void gl_list_node_set_value (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++/* Likewise. Returns 0 upon success, -1 upon out-of-memory. */ ++extern int gl_list_node_nx_set_value (gl_list_t list, gl_list_node_t node, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Returns the node immediately after the given node in the list, or NULL ++ if the given node is the last (rightmost) one in the list. */ ++extern gl_list_node_t gl_list_next_node (gl_list_t list, gl_list_node_t node); ++ ++/* Returns the node immediately before the given node in the list, or NULL ++ if the given node is the first (leftmost) one in the list. */ ++extern gl_list_node_t gl_list_previous_node (gl_list_t list, gl_list_node_t node); ++ ++/* Returns the first node in the list, or NULL if the list is empty. ++ This function is useful for iterating through the list like this: ++ gl_list_node_t node; ++ for (node = gl_list_first_node (list); node != NULL; node = gl_list_next_node (node)) ++ ... ++ */ ++extern gl_list_node_t gl_list_first_node (gl_list_t list); ++ ++/* Returns the last node in the list, or NULL if the list is empty. ++ This function is useful for iterating through the list in backward order, ++ like this: ++ gl_list_node_t node; ++ for (node = gl_list_last_node (list); node != NULL; node = gl_list_previous_node (node)) ++ ... ++ */ ++extern gl_list_node_t gl_list_last_node (gl_list_t list); ++ ++/* Returns the element at a given position in the list. ++ POSITION must be >= 0 and < gl_list_size (list). */ ++extern const void * gl_list_get_at (gl_list_t list, size_t position); ++ ++/* Returns the element at the first position in the list. ++ The list must be non-empty. */ ++extern const void * gl_list_get_first (gl_list_t list); ++ ++/* Returns the element at the last position in the list. ++ The list must be non-empty. */ ++extern const void * gl_list_get_last (gl_list_t list); ++ ++/* Replaces the element at a given position in the list. ++ POSITION must be >= 0 and < gl_list_size (list). ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_set_at (gl_list_t list, size_t position, ++ const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_set_at (gl_list_t list, size_t position, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Replaces the element at the first position in the list. ++ Returns its node. ++ The list must be non-empty. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_set_first (gl_list_t list, const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_set_first (gl_list_t list, const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Replaces the element at the last position in the list. ++ Returns its node. ++ The list must be non-empty. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_set_last (gl_list_t list, const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_set_last (gl_list_t list, const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Searches whether an element is already in the list. ++ Returns its node if found, or NULL if not present in the list. */ ++extern gl_list_node_t gl_list_search (gl_list_t list, const void *elt); ++ ++/* Searches whether an element is already in the list, ++ at a position >= START_INDEX. ++ Returns its node if found, or NULL if not present in the list. */ ++extern gl_list_node_t gl_list_search_from (gl_list_t list, size_t start_index, ++ const void *elt); ++ ++/* Searches whether an element is already in the list, ++ at a position >= START_INDEX and < END_INDEX. ++ Returns its node if found, or NULL if not present in the list. */ ++extern gl_list_node_t gl_list_search_from_to (gl_list_t list, ++ size_t start_index, ++ size_t end_index, ++ const void *elt); ++ ++/* Searches whether an element is already in the list. ++ Returns its position if found, or (size_t)(-1) if not present in the list. */ ++extern size_t gl_list_indexof (gl_list_t list, const void *elt); ++ ++/* Searches whether an element is already in the list, ++ at a position >= START_INDEX. ++ Returns its position if found, or (size_t)(-1) if not present in the list. */ ++extern size_t gl_list_indexof_from (gl_list_t list, size_t start_index, ++ const void *elt); ++ ++/* Searches whether an element is already in the list, ++ at a position >= START_INDEX and < END_INDEX. ++ Returns its position if found, or (size_t)(-1) if not present in the list. */ ++extern size_t gl_list_indexof_from_to (gl_list_t list, ++ size_t start_index, size_t end_index, ++ const void *elt); ++ ++/* Adds an element as the first element of the list. ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_add_first (gl_list_t list, const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_add_first (gl_list_t list, const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Adds an element as the last element of the list. ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_add_last (gl_list_t list, const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_add_last (gl_list_t list, const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Adds an element before a given element node of the list. ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_add_before (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_add_before (gl_list_t list, ++ gl_list_node_t node, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Adds an element after a given element node of the list. ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_add_after (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_add_after (gl_list_t list, gl_list_node_t node, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Adds an element at a given position in the list. ++ POSITION must be >= 0 and <= gl_list_size (list). */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_list_add_at (gl_list_t list, size_t position, ++ const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_list_nx_add_at (gl_list_t list, size_t position, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Removes an element from the list. ++ Returns true. */ ++extern bool gl_list_remove_node (gl_list_t list, gl_list_node_t node); ++ ++/* Removes an element at a given position from the list. ++ POSITION must be >= 0 and < gl_list_size (list). ++ Returns true. */ ++extern bool gl_list_remove_at (gl_list_t list, size_t position); ++ ++/* Removes the element at the first position from the list. ++ Returns true if it was found and removed, or false if the list was empty. */ ++extern bool gl_list_remove_first (gl_list_t list); ++ ++/* Removes the element at the last position from the list. ++ Returns true if it was found and removed, or false if the list was empty. */ ++extern bool gl_list_remove_last (gl_list_t list); ++ ++/* Searches and removes an element from the list. ++ Returns true if it was found and removed. */ ++extern bool gl_list_remove (gl_list_t list, const void *elt); ++ ++/* Frees an entire list. ++ (But this call does not free the elements of the list. It only invokes ++ the DISPOSE_FN on each of the elements of the list, and only if the list ++ is not a sublist.) */ ++extern void gl_list_free (gl_list_t list); ++ ++#endif /* End of inline and gl_xlist.h-defined functions. */ ++ ++/* --------------------- gl_list_iterator_t Data Type --------------------- */ ++ ++/* Functions for iterating through a list. */ ++ ++/* Type of an iterator that traverses a list. ++ This is a fixed-size struct, so that creation of an iterator doesn't need ++ memory allocation on the heap. */ ++typedef struct ++{ ++ /* For fast dispatch of gl_list_iterator_next. */ ++ const struct gl_list_implementation *vtable; ++ /* For detecting whether the last returned element was removed. */ ++ gl_list_t list; ++ size_t count; ++ /* Other, implementation-private fields. */ ++ void *p; void *q; ++ size_t i; size_t j; ++} gl_list_iterator_t; ++ ++#if 0 /* These are defined inline below. */ ++ ++/* Creates an iterator traversing a list. ++ The list contents must not be modified while the iterator is in use, ++ except for replacing or removing the last returned element. */ ++extern gl_list_iterator_t gl_list_iterator (gl_list_t list); ++ ++/* Creates an iterator traversing the element with indices i, ++ start_index <= i < end_index, of a list. ++ The list contents must not be modified while the iterator is in use, ++ except for replacing or removing the last returned element. */ ++extern gl_list_iterator_t gl_list_iterator_from_to (gl_list_t list, ++ size_t start_index, ++ size_t end_index); ++ ++/* If there is a next element, stores the next element in *ELTP, stores its ++ node in *NODEP if NODEP is non-NULL, advances the iterator and returns true. ++ Otherwise, returns false. */ ++extern bool gl_list_iterator_next (gl_list_iterator_t *iterator, ++ const void **eltp, gl_list_node_t *nodep); ++ ++/* Frees an iterator. */ ++extern void gl_list_iterator_free (gl_list_iterator_t *iterator); ++ ++#endif /* End of inline functions. */ ++ ++/* ---------------------- Sorted gl_list_t Data Type ---------------------- */ ++ ++/* The following functions are for lists without duplicates where the ++ order is given by a sort criterion. */ ++ ++/* Type of function used to compare two elements. Same as for qsort(). ++ NULL denotes pointer comparison. */ ++typedef int (*gl_listelement_compar_fn) (const void *elt1, const void *elt2); ++ ++#if 0 /* Unless otherwise specified, these are defined inline below. */ ++ ++/* Searches whether an element is already in the list. ++ The list is assumed to be sorted with COMPAR. ++ Returns its node if found, or NULL if not present in the list. ++ If the list contains several copies of ELT, the node of the leftmost one is ++ returned. */ ++extern gl_list_node_t gl_sortedlist_search (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ ++/* Searches whether an element is already in the list. ++ The list is assumed to be sorted with COMPAR. ++ Only list elements with indices >= START_INDEX and < END_INDEX are ++ considered; the implementation uses these bounds to minimize the number ++ of COMPAR invocations. ++ Returns its node if found, or NULL if not present in the list. ++ If the list contains several copies of ELT, the node of the leftmost one is ++ returned. */ ++extern gl_list_node_t gl_sortedlist_search_from_to (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t start_index, ++ size_t end_index, ++ const void *elt); ++ ++/* Searches whether an element is already in the list. ++ The list is assumed to be sorted with COMPAR. ++ Returns its position if found, or (size_t)(-1) if not present in the list. ++ If the list contains several copies of ELT, the position of the leftmost one ++ is returned. */ ++extern size_t gl_sortedlist_indexof (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ ++/* Searches whether an element is already in the list. ++ The list is assumed to be sorted with COMPAR. ++ Only list elements with indices >= START_INDEX and < END_INDEX are ++ considered; the implementation uses these bounds to minimize the number ++ of COMPAR invocations. ++ Returns its position if found, or (size_t)(-1) if not present in the list. ++ If the list contains several copies of ELT, the position of the leftmost one ++ is returned. */ ++extern size_t gl_sortedlist_indexof_from_to (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t start_index, ++ size_t end_index, ++ const void *elt); ++ ++/* Adds an element at the appropriate position in the list. ++ The list is assumed to be sorted with COMPAR. ++ Returns its node. */ ++/* declared in gl_xlist.h */ ++extern gl_list_node_t gl_sortedlist_add (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_list_node_t gl_sortedlist_nx_add (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt) ++ _GL_ATTRIBUTE_NODISCARD; ++ ++/* Searches and removes an element from the list. ++ The list is assumed to be sorted with COMPAR. ++ Returns true if it was found and removed. ++ If the list contains several copies of ELT, only the leftmost one is ++ removed. */ ++extern bool gl_sortedlist_remove (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ ++#endif /* End of inline and gl_xlist.h-defined functions. */ ++ ++/* ------------------------ Implementation Details ------------------------ */ ++ ++struct gl_list_implementation ++{ ++ /* gl_list_t functions. */ ++ gl_list_t (*nx_create_empty) (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates); ++ gl_list_t (*nx_create) (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates, ++ size_t count, const void **contents); ++ size_t (*size) (gl_list_t list); ++ const void * (*node_value) (gl_list_t list, gl_list_node_t node); ++ int (*node_nx_set_value) (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++ gl_list_node_t (*next_node) (gl_list_t list, gl_list_node_t node); ++ gl_list_node_t (*previous_node) (gl_list_t list, gl_list_node_t node); ++ gl_list_node_t (*first_node) (gl_list_t list); ++ gl_list_node_t (*last_node) (gl_list_t list); ++ const void * (*get_at) (gl_list_t list, size_t position); ++ gl_list_node_t (*nx_set_at) (gl_list_t list, size_t position, ++ const void *elt); ++ gl_list_node_t (*search_from_to) (gl_list_t list, size_t start_index, ++ size_t end_index, const void *elt); ++ size_t (*indexof_from_to) (gl_list_t list, size_t start_index, ++ size_t end_index, const void *elt); ++ gl_list_node_t (*nx_add_first) (gl_list_t list, const void *elt); ++ gl_list_node_t (*nx_add_last) (gl_list_t list, const void *elt); ++ gl_list_node_t (*nx_add_before) (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++ gl_list_node_t (*nx_add_after) (gl_list_t list, gl_list_node_t node, ++ const void *elt); ++ gl_list_node_t (*nx_add_at) (gl_list_t list, size_t position, ++ const void *elt); ++ bool (*remove_node) (gl_list_t list, gl_list_node_t node); ++ bool (*remove_at) (gl_list_t list, size_t position); ++ bool (*remove_elt) (gl_list_t list, const void *elt); ++ void (*list_free) (gl_list_t list); ++ /* gl_list_iterator_t functions. */ ++ gl_list_iterator_t (*iterator) (gl_list_t list); ++ gl_list_iterator_t (*iterator_from_to) (gl_list_t list, ++ size_t start_index, ++ size_t end_index); ++ bool (*iterator_next) (gl_list_iterator_t *iterator, ++ const void **eltp, gl_list_node_t *nodep); ++ void (*iterator_free) (gl_list_iterator_t *iterator); ++ /* Sorted gl_list_t functions. */ ++ gl_list_node_t (*sortedlist_search) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ gl_list_node_t (*sortedlist_search_from_to) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t start_index, ++ size_t end_index, ++ const void *elt); ++ size_t (*sortedlist_indexof) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ size_t (*sortedlist_indexof_from_to) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ size_t start_index, size_t end_index, ++ const void *elt); ++ gl_list_node_t (*sortedlist_nx_add) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++ bool (*sortedlist_remove) (gl_list_t list, ++ gl_listelement_compar_fn compar, ++ const void *elt); ++}; ++ ++struct gl_list_impl_base ++{ ++ const struct gl_list_implementation *vtable; ++ gl_listelement_equals_fn equals_fn; ++ gl_listelement_hashcode_fn hashcode_fn; ++ gl_listelement_dispose_fn dispose_fn; ++ bool allow_duplicates; ++}; ++ ++/* Define all functions of this file as accesses to the ++ struct gl_list_implementation. */ ++ ++GL_LIST_INLINE gl_list_t ++gl_list_nx_create_empty (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates) ++{ ++ return implementation->nx_create_empty (implementation, equals_fn, ++ hashcode_fn, dispose_fn, ++ allow_duplicates); ++} ++ ++GL_LIST_INLINE gl_list_t ++gl_list_nx_create (gl_list_implementation_t implementation, ++ gl_listelement_equals_fn equals_fn, ++ gl_listelement_hashcode_fn hashcode_fn, ++ gl_listelement_dispose_fn dispose_fn, ++ bool allow_duplicates, ++ size_t count, const void **contents) ++{ ++ return implementation->nx_create (implementation, equals_fn, hashcode_fn, ++ dispose_fn, allow_duplicates, count, ++ contents); ++} ++ ++GL_LIST_INLINE size_t ++gl_list_size (gl_list_t list) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->size (list); ++} ++ ++GL_LIST_INLINE const void * ++gl_list_node_value (gl_list_t list, gl_list_node_t node) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->node_value (list, node); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD int ++gl_list_node_nx_set_value (gl_list_t list, gl_list_node_t node, ++ const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->node_nx_set_value (list, node, elt); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_next_node (gl_list_t list, gl_list_node_t node) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->next_node (list, node); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_previous_node (gl_list_t list, gl_list_node_t node) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->previous_node (list, node); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_first_node (gl_list_t list) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->first_node (list); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_last_node (gl_list_t list) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->last_node (list); ++} ++ ++GL_LIST_INLINE const void * ++gl_list_get_at (gl_list_t list, size_t position) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->get_at (list, position); ++} ++ ++GL_LIST_INLINE const void * ++gl_list_get_first (gl_list_t list) ++{ ++ return gl_list_get_at (list, 0); ++} ++ ++GL_LIST_INLINE const void * ++gl_list_get_last (gl_list_t list) ++{ ++ return gl_list_get_at (list, gl_list_size (list) - 1); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_set_at (gl_list_t list, size_t position, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_set_at (list, position, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_set_first (gl_list_t list, const void *elt) ++{ ++ return gl_list_nx_set_at (list, 0, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_set_last (gl_list_t list, const void *elt) ++{ ++ return gl_list_nx_set_at (list, gl_list_size (list) - 1, elt); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_search (gl_list_t list, const void *elt) ++{ ++ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->search_from_to (list, 0, size, elt); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_search_from (gl_list_t list, size_t start_index, const void *elt) ++{ ++ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->search_from_to (list, start_index, size, elt); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_list_search_from_to (gl_list_t list, size_t start_index, size_t end_index, ++ const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->search_from_to (list, start_index, end_index, elt); ++} ++ ++GL_LIST_INLINE size_t ++gl_list_indexof (gl_list_t list, const void *elt) ++{ ++ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->indexof_from_to (list, 0, size, elt); ++} ++ ++GL_LIST_INLINE size_t ++gl_list_indexof_from (gl_list_t list, size_t start_index, const void *elt) ++{ ++ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->indexof_from_to (list, start_index, size, elt); ++} ++ ++GL_LIST_INLINE size_t ++gl_list_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, ++ const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->indexof_from_to (list, start_index, end_index, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_add_first (gl_list_t list, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_add_first (list, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_add_last (gl_list_t list, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_add_last (list, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_add_before (gl_list_t list, gl_list_node_t node, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_add_before (list, node, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_add_after (gl_list_t list, gl_list_node_t node, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_add_after (list, node, elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_list_nx_add_at (gl_list_t list, size_t position, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->nx_add_at (list, position, elt); ++} ++ ++GL_LIST_INLINE bool ++gl_list_remove_node (gl_list_t list, gl_list_node_t node) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->remove_node (list, node); ++} ++ ++GL_LIST_INLINE bool ++gl_list_remove_at (gl_list_t list, size_t position) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->remove_at (list, position); ++} ++ ++GL_LIST_INLINE bool ++gl_list_remove_first (gl_list_t list) ++{ ++ size_t size = gl_list_size (list); ++ if (size > 0) ++ return gl_list_remove_at (list, 0); ++ else ++ return false; ++} ++ ++GL_LIST_INLINE bool ++gl_list_remove_last (gl_list_t list) ++{ ++ size_t size = gl_list_size (list); ++ if (size > 0) ++ return gl_list_remove_at (list, size - 1); ++ else ++ return false; ++} ++ ++GL_LIST_INLINE bool ++gl_list_remove (gl_list_t list, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->remove_elt (list, elt); ++} ++ ++GL_LIST_INLINE void ++gl_list_free (gl_list_t list) ++{ ++ ((const struct gl_list_impl_base *) list)->vtable->list_free (list); ++} ++ ++GL_LIST_INLINE gl_list_iterator_t ++gl_list_iterator (gl_list_t list) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->iterator (list); ++} ++ ++GL_LIST_INLINE gl_list_iterator_t ++gl_list_iterator_from_to (gl_list_t list, size_t start_index, size_t end_index) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->iterator_from_to (list, start_index, end_index); ++} ++ ++GL_LIST_INLINE bool ++gl_list_iterator_next (gl_list_iterator_t *iterator, ++ const void **eltp, gl_list_node_t *nodep) ++{ ++ return iterator->vtable->iterator_next (iterator, eltp, nodep); ++} ++ ++GL_LIST_INLINE void ++gl_list_iterator_free (gl_list_iterator_t *iterator) ++{ ++ iterator->vtable->iterator_free (iterator); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_search (list, compar, elt); ++} ++ ++GL_LIST_INLINE gl_list_node_t ++gl_sortedlist_search_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_search_from_to (list, compar, start_index, end_index, ++ elt); ++} ++ ++GL_LIST_INLINE size_t ++gl_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_indexof (list, compar, elt); ++} ++ ++GL_LIST_INLINE size_t ++gl_sortedlist_indexof_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_indexof_from_to (list, compar, start_index, end_index, ++ elt); ++} ++ ++GL_LIST_INLINE _GL_ATTRIBUTE_NODISCARD gl_list_node_t ++gl_sortedlist_nx_add (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_nx_add (list, compar, elt); ++} ++ ++GL_LIST_INLINE bool ++gl_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) ++{ ++ return ((const struct gl_list_impl_base *) list)->vtable ++ ->sortedlist_remove (list, compar, elt); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++_GL_INLINE_HEADER_END ++ ++#endif /* _GL_LIST_H */ +-- +2.33.0 + diff --git a/backport-fix-CVE-2024-28835-gnutls_x509_trust_list_verify_crt2-remove-length-lim.patch b/backport-fix-CVE-2024-28835-gnutls_x509_trust_list_verify_crt2-remove-length-lim.patch new file mode 100644 index 0000000000000000000000000000000000000000..55121e010917254467fb9735a77557116aa86c65 --- /dev/null +++ b/backport-fix-CVE-2024-28835-gnutls_x509_trust_list_verify_crt2-remove-length-lim.patch @@ -0,0 +1,419 @@ +From e369e67a62f44561d417cb233acc566cc696d82d Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Mon, 29 Jan 2024 13:52:46 +0900 +Subject: [PATCH] gnutls_x509_trust_list_verify_crt2: remove length limit +of + input + +Previously, if cert_list_size exceeded DEFAULT_MAX_VERIFY_DEPTH, the +chain verification logic crashed with assertion failure. This patch +removes the restriction while keeping the maximum number of +retrieved certificates being DEFAULT_MAX_VERIFY_DEPTH. + +Signed-off-by: Daiki Ueno + +Reference: +https://gitlab.com/gnutls/gnutls/-/commit/e369e67a62f44561d417cb233acc566cc696d82d +Conflict:lib/x509/verify-high.c,tests/test-chains.h + +--- + lib/gnutls_int.h | 5 +- + lib/x509/common.c | 10 +- + lib/x509/verify-high.c | 56 +++++++---- + tests/test-chains.h | 210 ++++++++++++++++++++++++++++++++++++++++- + 4 files changed, 258 insertions(+), 23 deletions(-) + +diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h +index a06d123..9706db3 100644 +--- a/lib/gnutls_int.h ++++ b/lib/gnutls_int.h +@@ -218,7 +218,10 @@ typedef enum record_send_state_t { + + #define MAX_PK_PARAM_SIZE 2048 + +-/* defaults for verification functions ++/* Defaults for verification functions. ++ * ++ * update many_icas in tests/test-chains.h when increasing ++ * DEFAULT_MAX_VERIFY_DEPTH. + */ + #define DEFAULT_MAX_VERIFY_DEPTH 16 + #define DEFAULT_MAX_VERIFY_BITS (MAX_PK_PARAM_SIZE*8) +diff --git a/lib/x509/common.c b/lib/x509/common.c +index 96e7e7c..10ee9c6 100644 +--- a/lib/x509/common.c ++++ b/lib/x509/common.c +@@ -1746,7 +1746,15 @@ unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist, + bool insorted[DEFAULT_MAX_VERIFY_DEPTH]; /* non zero if clist[i] used in sorted list */ + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + +- assert(clist_size <= DEFAULT_MAX_VERIFY_DEPTH); ++ /* Limit the number of certificates in the chain, to avoid DoS ++ * because of the O(n^2) sorting below. FIXME: Switch to a ++ * topological sort algorithm which should be linear to the ++ * number of certificates and subject-issuer relationships. ++ */ ++ if (clist_size > DEFAULT_MAX_VERIFY_DEPTH) { ++ _gnutls_debug_log("too many certificates; skipping sorting\n"); ++ return 1; ++ } + + for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) { + issuer[i] = -1; +diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c +index 02d2f0f..bbfb02e 100644 +--- a/lib/x509/verify-high.c ++++ b/lib/x509/verify-high.c +@@ -25,7 +25,7 @@ + #include "errors.h" + #include + #include +-#include /* MAX */ ++#include "num.h" /* MIN */ + #include + #include + #include +@@ -1357,7 +1357,8 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + int ret = 0; + unsigned int i; + size_t hash; +- gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; ++ gnutls_x509_crt_t *cert_list_copy = NULL; ++ unsigned int cert_list_max_size = 0; + gnutls_x509_crt_t retrieved[DEFAULT_MAX_VERIFY_DEPTH]; + unsigned int retrieved_size = 0; + const char *hostname = NULL, *purpose = NULL, *email = NULL; +@@ -1411,15 +1412,27 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + } + } + +- memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t)); +- cert_list = sorted; ++ /* Allocate extra for retrieved certificates. */ ++ if (!INT_ADD_OK(cert_list_size, DEFAULT_MAX_VERIFY_DEPTH, ++ &cert_list_max_size)) ++ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + +- records = gl_list_nx_create_empty(GL_LINKEDHASH_LIST, cert_eq, cert_hashcode, NULL, false); +- if (records == NULL) ++ cert_list_copy = _gnutls_reallocarray(NULL, cert_list_max_size, ++ sizeof(gnutls_x509_crt_t)); ++ if (!cert_list_copy) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + +- for (i = 0; i < cert_list_size && +- cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) { ++ memcpy(cert_list_copy, cert_list, ++ cert_list_size * sizeof(gnutls_x509_crt_t)); ++ cert_list = cert_list_copy; ++ ++ records = gl_list_nx_create_empty(GL_LINKEDHASH_LIST, cert_eq, cert_hashcode, NULL, false); ++ if (records == NULL) { ++ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ goto cleanup; ++ } ++ ++ for (i = 0; i < cert_list_size;) { + unsigned int sorted_size = 1; + unsigned int j, k; + gnutls_x509_crt_t issuer; +@@ -1431,8 +1444,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + + assert(sorted_size > 0); + +- /* Remove duplicates. Start with index 1, as the first element +- * may be re-checked after issuer retrieval. */ ++ /* Remove duplicates. */ + for (j = 0; j < sorted_size; j++) { + if (gl_list_search(records, cert_list[i + j])) { + if (i + j < cert_list_size - 1) { +@@ -1483,17 +1495,16 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + continue; + } + +- ret = retrieve_issuers(list, +- cert_list[i - 1], +- &retrieved[retrieved_size], +- DEFAULT_MAX_VERIFY_DEPTH - +- MAX(retrieved_size, +- cert_list_size)); ++ ret = retrieve_issuers(list, cert_list[i - 1], &retrieved[retrieved_size], ++ MIN(DEFAULT_MAX_VERIFY_DEPTH - retrieved_size, ++ cert_list_max_size - cert_list_size)); + if (ret < 0) { + break; + } else if (ret > 0) { + assert((unsigned int)ret <= +- DEFAULT_MAX_VERIFY_DEPTH - cert_list_size); ++ DEFAULT_MAX_VERIFY_DEPTH - retrieved_size); ++ assert((unsigned int)ret <= ++ cert_list_max_size - cert_list_size); + memmove(&cert_list[i + ret], + &cert_list[i], + (cert_list_size - i) * +@@ -1511,8 +1522,10 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + } + + cert_list_size = shorten_clist(list, cert_list, cert_list_size); +- if (cert_list_size <= 0) +- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ++ if (cert_list_size <= 0) { ++ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ++ goto cleanup; ++ } + + hash = + hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn. +@@ -1663,10 +1676,13 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + } + + cleanup: ++ gnutls_free(cert_list_copy); + for (i = 0; i < retrieved_size; i++) { + gnutls_x509_crt_deinit(retrieved[i]); + } +- gl_list_free(records); ++ if (records) { ++ gl_list_free(records); ++ } + return ret; + } + +diff --git a/tests/test-chains.h b/tests/test-chains.h +index 07e90bb..2dbee07 100644 +--- a/tests/test-chains.h ++++ b/tests/test-chains.h +@@ -25,7 +25,7 @@ + + /* *INDENT-OFF* */ + +-#define MAX_CHAIN 10 ++#define MAX_CHAIN 17 + + static const char *chain_with_no_subject_id_in_ca_ok[] = { + "-----BEGIN CERTIFICATE-----\n" +@@ -4386,6 +4386,213 @@ static const char *cross_signed_ca[] = { + NULL + }; + ++/* This assumes DEFAULT_MAX_VERIFY_DEPTH to be 16 */ ++static const char *many_icas[] = { ++ /* Server */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBqzCCAV2gAwIBAgIUIK3+SD3GmqJlRLZ/ESyhTzkSDL8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowNzEbMBkGA1UEChMSR251VExTIHRlc3Qgc2VydmVyMRgwFgYD\n" ++ "VQQDEw90ZXN0LmdudXRscy5vcmcwKjAFBgMrZXADIQAWGjx45NIJiKFsNBxxRRjm\n" ++ "NxUT5KYK7xXr5HPVywwgLaOBkjCBjzAMBgNVHRMBAf8EAjAAMBoGA1UdEQQTMBGC\n" ++ "D3Rlc3QuZ251dGxzLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8E\n" ++ "BAMCB4AwHQYDVR0OBBYEFKgNAQWZPx76/vXqQOdIi5mTftsaMB8GA1UdIwQYMBaA\n" ++ "FDaPsY6WAGuRtrhYJE6Gk/bg5qbdMAUGAytlcANBAMIDh8aGcIIFDTUrzfV7tnkX\n" ++ "hHrxyFKBH/cApf6xcJQTfDXm23po627Ibp+WgLaWMY08Fn9Y2V6Ev8ADfqXNbQ8=\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA16 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUSnE0PKdm/dsnZSWBh5Ct4pS6DcwwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAxq9SI8vp0QH1dDBBuZW+t+bLLROppQbjSQ4O1BEonDOjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ2j7GOlgBrkba4\n" ++ "WCROhpP24Oam3TAfBgNVHSMEGDAWgBRvdUKX0aw3nfUIdvivXGSfRO7zyjAFBgMr\n" ++ "ZXADQQBsI2Hc7X5hXoHTvk01qMc5a1I27QHAFRARJnvIQ15wxNS2LVLzGk+AUmwr\n" ++ "sOhBKAcVfS55uWtYdjoWQ80h238H\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA15 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUQk4XkgQVImnp6OPZas7ctwgBza4wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAs3yVKLJd3sKbNVmj6Bxy2j1x025rksyQpZZWnCx5a+CjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRvdUKX0aw3nfUI\n" ++ "dvivXGSfRO7zyjAfBgNVHSMEGDAWgBRhGfUXYPh4YQsdtTWYUozLphGgfzAFBgMr\n" ++ "ZXADQQBXTtm56x6/pHXdW8dTvZLc/8RufNQrMlc23TCgX0apUnrZdTsNAb7OE4Uu\n" ++ "9PBuxK+CC9NL/BL2hXsKvAT+NWME\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA14 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUKfwz7UUYRvYlvqwmnLJlTOS9o1AwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAXbUetQ08t+F4+IcKL++HpeclqTxXZ7cG4mwqvHmTUEWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRhGfUXYPh4YQsd\n" ++ "tTWYUozLphGgfzAfBgNVHSMEGDAWgBQYRQqO+V1kefF7QvNnFU1fX5H9+jAFBgMr\n" ++ "ZXADQQAiSHNMTLPFP3oa6q13Dj8jSxF9trQDJGM1ArWffFcPZUt2U4/ODHdcMTHx\n" ++ "kGwhIj+ghBlu6ykgu6J2wewCUooC\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA13 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUUKOs59gyCPAZzoC7zMZQSh6AnQgwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAmvqhj5GYqsXIpsr1BXBfD+2mTP/m/TEpKIYSZHM62dijYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQYRQqO+V1kefF7\n" ++ "QvNnFU1fX5H9+jAfBgNVHSMEGDAWgBQ27HzvP5hl2xR+LOzRcPfmY5ndXjAFBgMr\n" ++ "ZXADQQBrB3NkrYC7EQ74qgeesVOE71rW012dPOOKPAV0laR+JLEgsv9sfus+AdBF\n" ++ "WBNwR3KeYBTi/MFDuecxBHU2m5gD\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA12 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUUQooGfH21+sR7/pSgCWm13gg2H4wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAK2of/B4wMpk6k/KdugC5dMS+jo2fseUM7/PvXkE6HASjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ27HzvP5hl2xR+\n" ++ "LOzRcPfmY5ndXjAfBgNVHSMEGDAWgBSJDHU0Mj1Xr0e8ErCnRK24w7XwTTAFBgMr\n" ++ "ZXADQQDY8d2bAZpj7oGhdl2dBsCE48jEWj49da0PbgN12koAj3gf4hjMPd8G7p5z\n" ++ "8RsURAwQmCkE8ShvdNw/Qr2tDL0E\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA11 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUW9Dw0hU2pfjXhb5Stip+mk9SndIwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAn5ISjLVV6RBWsnxDWHDicpye7SjFwGOTwzF01/psiJ2jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSJDHU0Mj1Xr0e8\n" ++ "ErCnRK24w7XwTTAfBgNVHSMEGDAWgBSR9UU27RI0XohiEgHDxNo/9HP4djAFBgMr\n" ++ "ZXADQQCfQg6MDHk71vhyrEo4/5PcLb2Li5F/FKURyux7snv2TbkSdInloAqca9UR\n" ++ "DtqHSLCNLXCNdSPr5QwIt5p29rsE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA10 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUR4uTedG8e6MibKViQ3eX7QzXG1swBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAnslX04kSVOL5LAf1e+Ze3ggNnDJcEAxLDk8I/IhyjTyjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSR9UU27RI0Xohi\n" ++ "EgHDxNo/9HP4djAfBgNVHSMEGDAWgBRC7US5gJYnvd5F7EN+C4anMgd2NzAFBgMr\n" ++ "ZXADQQDo+jHt07Tvz3T5Lbz6apBrSln8xKYfJk2W1wP85XAnf7sZT9apM1bS4EyD\n" ++ "Kckw+KG+9x7myOZz6AXJgZB5OGAO\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA9 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUSIIIRjrNpE+kEPkiJMOqaNAazvQwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAZKy7p1Gn4W/reRxKJN99+QkHt2q9aELktCKe5PqrX5ejYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRC7US5gJYnvd5F\n" ++ "7EN+C4anMgd2NzAfBgNVHSMEGDAWgBSOhR7Ornis2x8g0J+bvTTwMnW60zAFBgMr\n" ++ "ZXADQQA0MEcC4FgKZEAfalVpApU2to0G158MVz/WTNcSc7fnl8ifJ/g56dVHL1jr\n" ++ "REvC/S28dn/CGAlbVXUAgxnHAbgE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA8 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUGGFSgD95vOTSj7iFxfXA5vq6vsYwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAg3W/bTdW0fR32NeZEVMXICpa30d7rSdddLOYDvqqUO+jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSOhR7Ornis2x8g\n" ++ "0J+bvTTwMnW60zAfBgNVHSMEGDAWgBT3zK8Hbn9aVTAOOFY6RSxJ2o5x2jAFBgMr\n" ++ "ZXADQQBl4gnzE463iMFg57gPvjHdVzA39sJBpiu0kUGfRcLnoRI/VOaLcx7WnJ9+\n" ++ "c3KxPZBec76EdIoQDkTmI6m2FIAM\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA7 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUGktMGXhNuaMhKyAlecymmLD+/GIwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEA/Z1oc76hOQ0Hi+2hePaGIntnMIDqBlb7RDMjRpYONP2jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBT3zK8Hbn9aVTAO\n" ++ "OFY6RSxJ2o5x2jAfBgNVHSMEGDAWgBSPae3JUN3jP0NgUJqDV3eYxcaM3DAFBgMr\n" ++ "ZXADQQBMkwKaUZlvG/hax8rv3nnDv8kJOr6KVHBnxSx3hZ+8HIBT7GFm1+YDeYOB\n" ++ "jhNg66kyeFPGXXBCe+mvNQFFjCEE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA6 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUKn3gz5lAUpKqWlHKLKYDbOJ4rygwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAZ/eD4eTe91ddvHusm7YlLPxU4ByGFc6suAmlP1CxXkWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSPae3JUN3jP0Ng\n" ++ "UJqDV3eYxcaM3DAfBgNVHSMEGDAWgBT9f/qSI/jhxvGI7aMtkpraDcjBnjAFBgMr\n" ++ "ZXADQQAMRnkmRhnLGdmJaY8B42gfyaAsqCMyds/Tw4OHYy+N48XuAxRjKkhf3szC\n" ++ "0lY71oU043mNP1yx/dzAuCTrVSgI\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA5 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUEgEYbBXXEyGv3vOq10JQv1SBiUUwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAs2xEDPw8RVal53nX9GVwUd1blq1wjtVFC8S1V7up7MWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBT9f/qSI/jhxvGI\n" ++ "7aMtkpraDcjBnjAfBgNVHSMEGDAWgBRBVkLu9BmCKz7HNI8md4vPpoE/7jAFBgMr\n" ++ "ZXADQQCCufAyLijtzzmeCuO3K50rBSbGvB3FQfep7g6kVsQKM3bw/olWK5/Ji0dD\n" ++ "ubJ0cFl1FmfAda7aVxLBtJOvO6MI\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA4 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIULj8GkaHw+92HuOTnXnXlxCy3VrEwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAiedxh4dvtwDellMAHc/pZH0MAOXobRenTUgF1yj5l12jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRBVkLu9BmCKz7H\n" ++ "NI8md4vPpoE/7jAfBgNVHSMEGDAWgBSDtNRgQ36KwW/ASaMyr6WeDt0STDAFBgMr\n" ++ "ZXADQQDL8U2ckzur7CktdrVUNvfLhVCOz33d/62F28vQFHUa8h/4h+Mi1MMbXOKT\n" ++ "1bL2TvpFpU7Fx/vcIPXDielVqr4C\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA3 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUQXl74TDDw6MQRMbQUSPa6Qrvba8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEA7l0jQ0f4fJRw7Qja/Hz2qn8y91SI7CokxhSf+FT+9M6jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSDtNRgQ36KwW/A\n" ++ "SaMyr6WeDt0STDAfBgNVHSMEGDAWgBQ2inEK4KH6ATftmybxKE1dZUzOozAFBgMr\n" ++ "ZXADQQCnP7Oqx1epGnFnO7TrTJwcUukXDEYsINve2GeUsi8HEIeKKlMcLZ2Cnaj7\n" ++ "5v9NGuWh3QJpmmSGpEemiv8dJc4A\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA2 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUP7Nmof8H2F1LyDkjqlYIUpGdXE8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAkW9Rod3CXAnha6nlaHkDbCOegq94lgmjqclA9sOIt3yjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ2inEK4KH6ATft\n" ++ "mybxKE1dZUzOozAfBgNVHSMEGDAWgBRPq/CQlK/zuXkjZvTCibu+vejD+jAFBgMr\n" ++ "ZXADQQBU+A+uF0yrtO/yv9cRUdCoL3Y1NKM35INg8BQDnkv724cW9zk1x0q9Fuou\n" ++ "zvfSVb8S3vT8fF5ZDOxarQs6ZH0C\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA1 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBXTCCAQ+gAwIBAgIUfUWP+AQHpdFTRKTf21mMzjaJsp0wBQYDK2VwMBkxFzAV\n" ++ "BgNVBAMTDkdudVRMUyB0ZXN0IENBMCAXDTI0MDMxMjIyNTMzOVoYDzk5OTkxMjMx\n" ++ "MjM1OTU5WjAdMRswGQYDVQQDDBJHbnVUTFMgdGVzdCBJQ0EgJGkwKjAFBgMrZXAD\n" ++ "IQAVmfBAvLbT+pTD24pQrr6S0jEIFIV/qOv93yYvAUzpzKNjMGEwDwYDVR0TAQH/\n" ++ "BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0OBBYEFE+r8JCUr/O5eSNm9MKJ\n" ++ "u7696MP6MB8GA1UdIwQYMBaAFAFpt5wrFsqCtHc4PpluPDvwcxQLMAUGAytlcANB\n" ++ "AC6+XZnthjlUD0TbBKRF3qT5if3Pp29Bgvutw8859unzUZW8FkHg5KeDBj9ncgJc\n" ++ "O2tFnNH2hV6LDPJzU0rtLQc=\n" ++ "-----END CERTIFICATE-----\n", ++ NULL ++}; ++ ++static const char *many_icas_ca[] = { ++ /* CA (self-signed) */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBNzCB6qADAgECAhRjaokcQwcrtW8tjuVFz3A33F8POjAFBgMrZXAwGTEXMBUG\n" ++ "A1UEAxMOR251VExTIHRlc3QgQ0EwIBcNMjQwMzEyMjI1MzM5WhgPOTk5OTEyMzEy\n" ++ "MzU5NTlaMBkxFzAVBgNVBAMTDkdudVRMUyB0ZXN0IENBMCowBQYDK2VwAyEAvoxP\n" ++ "TNdbWktxA8qQNNH+25Cx9rzP+DxLGeI/7ODwrQGjQjBAMA8GA1UdEwEB/wQFMAMB\n" ++ "Af8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQBabecKxbKgrR3OD6Zbjw78HMU\n" ++ "CzAFBgMrZXADQQCP5IUD74M7WrUx20uqzrzuj+s2jnBVmLQfWf/Ucetx+oTRFeq4\n" ++ "xZB/adWhycSeJUAB1zKqYUV9hgT8FWHbnHII\n" ++ "-----END CERTIFICATE-----\n", ++ NULL ++}; ++ + #if defined __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + # pragma GCC diagnostic push + # pragma GCC diagnostic ignored "-Wunused-variable" +@@ -4566,6 +4773,7 @@ static struct + GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM), + GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID, NULL, 1620118136, 1}, + { "cross signed - ok", cross_signed, cross_signed_ca, 0, 0, 0, 1704955300}, ++ { "many intermediates - ok", many_icas, many_icas_ca, 0, 0, 0, 1710284400}, + { NULL, NULL, NULL, 0, 0} + }; + +-- +2.33.0 + diff --git a/backport-lib-suppress-false-positive-Wanalyzer-out-of-bounds.patch b/backport-lib-suppress-false-positive-Wanalyzer-out-of-bounds.patch new file mode 100644 index 0000000000000000000000000000000000000000..e4c94ffa23218eface2890ee15b08235c52df709 --- /dev/null +++ b/backport-lib-suppress-false-positive-Wanalyzer-out-of-bounds.patch @@ -0,0 +1,70 @@ +From 7e7b8ed89c93b5f95367eeab1b4f06fc2ac83581 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Wed, 7 Jun 2023 16:44:00 +0200 +Subject: [PATCH] lib: suppress false-positive -Wanalyzer-out-of-bounds + +GCC analyzer from GCC 13 reports this: + + verify-high.c:1471:21: error: stack-based buffer over-read [CWE-126] [-Werror=analyzer-out-of-bounds] + 1471 | if (gnutls_x509_trust_list_get_issuer( + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 1472 | list, cert_list[i - 1], &issuer, + +This is false-positive, as i is always in a range 0 < i < cert_list_size. + +Signed-off-by: Daiki Ueno + +Reference: https://gitlab.com/gnutls/gnutls/-/commit/7e7b8ed89c93b5f95367eeab1b4f06fc2ac83581 +Conflict: NA + +--- + lib/x509/verify-high.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c +index 2c070b0..02d2f0f 100644 +--- a/lib/x509/verify-high.c ++++ b/lib/x509/verify-high.c +@@ -1421,7 +1421,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + for (i = 0; i < cert_list_size && + cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) { + unsigned int sorted_size = 1; +- unsigned int j; ++ unsigned int j, k; + gnutls_x509_crt_t issuer; + + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) { +@@ -1429,6 +1429,8 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + cert_list_size - i); + } + ++ assert(sorted_size > 0); ++ + /* Remove duplicates. Start with index 1, as the first element + * may be re-checked after issuer retrieval. */ + for (j = 0; j < sorted_size; j++) { +@@ -1448,13 +1450,20 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + } + + /* Record the certificates seen. */ +- for (j = 0; j < sorted_size; j++, i++) { ++ for (k = 0; k < sorted_size; k++, i++) { + if (!gl_list_nx_add_last(records, cert_list[i])) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + } + ++ /* Pacify GCC analyzer: the condition always holds ++ * true as sorted_size > 0 is checked above, and the ++ * following loop should iterate at least once so i++ ++ * is called. ++ */ ++ assert(i > 0); ++ + /* If the issuer of the certificate is known, no need + * for further processing. */ + if (gnutls_x509_trust_list_get_issuer(list, +-- +2.33.0 + diff --git a/backport-x509-fix-thread-safety-in-gnutls_x509_trust_list_ver.patch b/backport-x509-fix-thread-safety-in-gnutls_x509_trust_list_ver.patch new file mode 100644 index 0000000000000000000000000000000000000000..17581b47ab11aa8a077a2881ae1bd53bf1a54874 --- /dev/null +++ b/backport-x509-fix-thread-safety-in-gnutls_x509_trust_list_ver.patch @@ -0,0 +1,49 @@ +From 22f837ba0bc7d13c3d738a8583566368fc12aee1 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Sat, 30 Oct 2021 08:56:07 +0200 +Subject: [PATCH] x509: fix thread-safety in gnutls_x509_trust_list_verify_crt2 + +This function previously used gnutls_x509_trust_list_get_issuer +without GNUTLS_TL_GET_COPY flag, which is required when the function +is called from multi-threaded application and PKCS #11 trust store is +in use. + +Reported and the change suggested by Remi Gacogne in: +https://gitlab.com/gnutls/gnutls/-/issues/1277 + +Signed-off-by: Daiki Ueno + +Reference: https://gitlab.com/gnutls/gnutls/-/commit/22f837ba0bc7d13c3d738a8583566368fc12aee1 +Conflict: NA + +--- + lib/x509/verify-high.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c +index ab8e006ca..5698d4f37 100644 +--- a/lib/x509/verify-high.c ++++ b/lib/x509/verify-high.c +@@ -1102,7 +1102,8 @@ int trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list, + * gnutls_x509_trust_list_get_issuer: + * @list: The list + * @cert: is the certificate to find issuer for +- * @issuer: Will hold the issuer if any. Should be treated as constant. ++ * @issuer: Will hold the issuer if any. Should be treated as constant ++ * unless %GNUTLS_TL_GET_COPY is set in @flags. + * @flags: flags from %gnutls_trust_list_flags_t (%GNUTLS_TL_GET_COPY is applicable) + * + * This function will find the issuer of the given certificate. +@@ -1521,7 +1522,8 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + if (gnutls_x509_trust_list_get_issuer(list, + cert_list[i - 1], + &issuer, +- 0) == 0) { ++ GNUTLS_TL_GET_COPY) == 0) { ++ gnutls_x509_crt_deinit(issuer); + cert_list_size = i; + break; + } +-- +2.33.0 + diff --git a/gnutls.spec b/gnutls.spec index 726a09ca6f5c19a0c1c9035d16076d1044792b45..e899f52cd153cc27d3f6cd70b8e854cc733ff654 100644 --- a/gnutls.spec +++ b/gnutls.spec @@ -1,6 +1,6 @@ Name: gnutls Version: 3.7.2 -Release: 13 +Release: 15 Summary: The GNU Secure Communication Protocol Library License: LGPLv2.1+ and GPLv3+ @@ -18,6 +18,11 @@ Patch7: backport-CVE-2023-5981-auth-rsa_psk-side-step-potential-side-channel.pat Patch8: backport-CVE-2024-0553-rsa-psk-minimize-branching-after-decryption.patch Patch9: backport-CVE-2024-0567-x509-detect-loop-in-certificate-chain.patch Patch10: backport-fix-CVE-2024-28834-nettle-avoid-normalization-of-mpz_t-in-deterministic.patch +Patch11: backport-add-gnulib-files.patch +Patch12: backport-x509-fix-thread-safety-in-gnutls_x509_trust_list_ver.patch +Patch13: backport-Fix-removal-of-duplicate-certs-during-verification.patch +Patch14: backport-lib-suppress-false-positive-Wanalyzer-out-of-bounds.patch +Patch15: backport-fix-CVE-2024-28835-gnutls_x509_trust_list_verify_crt2-remove-length-lim.patch %bcond_without dane %bcond_with guile @@ -224,6 +229,12 @@ make check %{?_smp_mflags} %endif %changelog +* Thu Apr 18 2024 fangxiuning - 3.7.2-15 +- fix CVE-2024-28835 + +* Thu Apr 18 2024 fangxiuning - 3.7.2-14 +- fix CVE-2024-28835 + * Tue Mar 26 2024 xuraoqing - 3.7.2-13 - update patch to remove function declare in header file