From 8e82432ef8bbfe0071d6c8f9622f380ca3562996 Mon Sep 17 00:00:00 2001 From: purplerain Date: Thu, 14 Mar 2024 20:22:48 +0000 Subject: [PATCH] sync with OpenBSD -current --- lib/libexpat/Changes | 6 + lib/libexpat/lib/xmlparse.c | 6 +- lib/libexpat/tests/acc_tests.c | 59 ++++++++ regress/sys/Makefile | 4 +- usr.bin/ssh/ssh.1 | 5 +- usr.sbin/btrace/ksyms.c | 248 +++++++++++++++------------------ 6 files changed, 184 insertions(+), 144 deletions(-) diff --git a/lib/libexpat/Changes b/lib/libexpat/Changes index a7d4caf9a..48df93dc0 100644 --- a/lib/libexpat/Changes +++ b/lib/libexpat/Changes @@ -2,6 +2,12 @@ NOTE: We are looking for help with a few things: https://github.com/libexpat/libexpat/labels/help%20wanted If you can help, please get in touch. Thanks! + Security fixes: + #839 #842 CVE-2024-28757 -- Prevent billion laughs attacks with + isolated use of external parsers. Please see the commit + message of commit 1d50b80cf31de87750103656f6eb693746854aa8 + for details. + Release 2.6.0 Tue February 6 2024 Security fixes: #789 #814 CVE-2023-52425 -- Fix quadratic runtime issues with big tokens diff --git a/lib/libexpat/lib/xmlparse.c b/lib/libexpat/lib/xmlparse.c index aaf0fa9c8..dfaa527af 100644 --- a/lib/libexpat/lib/xmlparse.c +++ b/lib/libexpat/lib/xmlparse.c @@ -7779,6 +7779,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { static float accountingGetCurrentAmplification(XML_Parser rootParser) { + // 1.........1.........12 => 22 + const size_t lenOfShortestInclude = sizeof("") - 1; const XmlBigCount countBytesOutput = rootParser->m_accounting.countBytesDirect + rootParser->m_accounting.countBytesIndirect; @@ -7786,7 +7788,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { = rootParser->m_accounting.countBytesDirect ? (countBytesOutput / (float)(rootParser->m_accounting.countBytesDirect)) - : 1.0f; + : ((lenOfShortestInclude + + rootParser->m_accounting.countBytesIndirect) + / (float)lenOfShortestInclude); assert(! rootParser->m_parentParser); return amplificationFactor; } diff --git a/lib/libexpat/tests/acc_tests.c b/lib/libexpat/tests/acc_tests.c index e1c4b7f7e..f193aa58a 100644 --- a/lib/libexpat/tests/acc_tests.c +++ b/lib/libexpat/tests/acc_tests.c @@ -378,6 +378,63 @@ START_TEST(test_helper_unsigned_char_to_printable) { fail("unsignedCharToPrintable result mistaken"); } END_TEST + +START_TEST(test_amplification_isolated_external_parser) { + // NOTE: Length 44 is precisely twice the length of "" + // (22) that is used in function accountingGetCurrentAmplification in + // xmlparse.c. + // 1.........1.........1.........1.........1..4 => 44 + const char doc[] = ""; + const int docLen = (int)sizeof(doc) - 1; + const float maximumToleratedAmplification = 2.0f; + + struct TestCase { + int offsetOfThreshold; + enum XML_Status expectedStatus; + }; + + struct TestCase cases[] = { + {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR}, + {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK}, + }; + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + const int offsetOfThreshold = cases[i].offsetOfThreshold; + const enum XML_Status expectedStatus = cases[i].expectedStatus; + const unsigned long long activationThresholdBytes + = docLen + offsetOfThreshold; + + set_subtest("offsetOfThreshold=%d, expectedStatus=%d", offsetOfThreshold, + expectedStatus); + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + assert_true(XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, maximumToleratedAmplification) + == XML_TRUE); + assert_true(XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, activationThresholdBytes) + == XML_TRUE); + + XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); + assert_true(ext_parser != NULL); + + const enum XML_Status actualStatus + = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE); + + assert_true(actualStatus == expectedStatus); + if (actualStatus != XML_STATUS_OK) { + assert_true(XML_GetErrorCode(ext_parser) + == XML_ERROR_AMPLIFICATION_LIMIT_BREACH); + } + + XML_ParserFree(ext_parser); + XML_ParserFree(parser); + } +} +END_TEST + #endif // XML_GE == 1 void @@ -390,6 +447,8 @@ make_accounting_test_case(Suite *s) { tcase_add_test(tc_accounting, test_accounting_precision); tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api); tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable); + tcase_add_test__ifdef_xml_dtd(tc_accounting, + test_amplification_isolated_external_parser); #else UNUSED_P(s); #endif /* XML_GE == 1 */ diff --git a/regress/sys/Makefile b/regress/sys/Makefile index ff0075e51..d217ab4e4 100644 --- a/regress/sys/Makefile +++ b/regress/sys/Makefile @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile,v 1.27 2022/10/22 17:50:28 gkoehler Exp $ +# $OpenBSD: Makefile,v 1.28 2024/03/14 14:29:03 kettenis Exp $ # $NetBSD: Makefile,v 1.4 1995/04/20 22:41:08 cgd Exp $ -SUBDIR= altivec_ast copy crypto dev ddb ffs \ +SUBDIR= altivec_ast btcfi copy crypto dev ddb ffs \ fifofs fileops kern mfs_noperm \ net netinet netinet6 nfs ptrace sys uvm .if exists(arch/${MACHINE}/Makefile) diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1 index 936c995ba..4f5fd07a6 100644 --- a/usr.bin/ssh/ssh.1 +++ b/usr.bin/ssh/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.438 2023/10/11 23:14:33 djm Exp $ -.Dd $Mdocdate: October 11 2023 $ +.\" $OpenBSD: ssh.1,v 1.439 2024/03/14 06:23:14 job Exp $ +.Dd $Mdocdate: March 14 2024 $ .Dt SSH 1 .Os .Sh NAME @@ -331,6 +331,7 @@ connection to the jump host described by and then establishing a TCP forwarding to the ultimate destination from there. Multiple jump hops may be specified separated by comma characters. +IPv6 addresses can be specified by enclosing the address in square brackets. This is a shortcut to specify a .Cm ProxyJump configuration directive. diff --git a/usr.sbin/btrace/ksyms.c b/usr.sbin/btrace/ksyms.c index 27542efd3..5d1ea4576 100644 --- a/usr.sbin/btrace/ksyms.c +++ b/usr.sbin/btrace/ksyms.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ksyms.c,v 1.7 2024/03/12 17:22:24 cheloha Exp $ */ +/* $OpenBSD: ksyms.c,v 1.8 2024/03/14 00:54:54 cheloha Exp $ */ /* * Copyright (c) 2016 Martin Pieuchot @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -31,149 +30,60 @@ #include "btrace.h" -struct sym { - char *sym_name; - unsigned long sym_value; /* from st_value */ - unsigned long sym_size; /* from st_size */ -}; - struct syms { - struct sym *table; - size_t nsymb; + int fd; + Elf *elf; + Elf_Scn *symtab; + size_t strtabndx, nsymb; }; -int sym_compare_search(const void *, const void *); -int sym_compare_sort(const void *, const void *); +int kelf_parse(struct syms *); struct syms * kelf_open(const char *path) { - char *name; - Elf *elf; - Elf_Data *data = NULL; - Elf_Scn *scn = NULL, *symtab; - GElf_Sym sym; - GElf_Shdr shdr; - size_t i, shstrndx, strtabndx = SIZE_MAX, symtab_size; - unsigned long diff; - struct sym *tmp; - struct syms *syms = NULL; - int fd; + struct syms *syms; + int error; if (elf_version(EV_CURRENT) == EV_NONE) errx(1, "elf_version: %s", elf_errmsg(-1)); - fd = open(path, O_RDONLY); - if (fd == -1) { + if ((syms = calloc(1, sizeof(*syms))) == NULL) + err(1, NULL); + + syms->fd = open(path, O_RDONLY); + if (syms->fd == -1) { warn("open: %s", path); + free(syms); return NULL; } - if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + if ((syms->elf = elf_begin(syms->fd, ELF_C_READ, NULL)) == NULL) { warnx("elf_begin: %s", elf_errmsg(-1)); goto bad; } - if (elf_kind(elf) != ELF_K_ELF) + if (elf_kind(syms->elf) != ELF_K_ELF) goto bad; - if (elf_getshdrstrndx(elf, &shstrndx) != 0) { - warnx("elf_getshdrstrndx: %s", elf_errmsg(-1)); - goto bad; - } - - while ((scn = elf_nextscn(elf, scn)) != NULL) { - if (gelf_getshdr(scn, &shdr) != &shdr) { - warnx("elf_getshdr: %s", elf_errmsg(-1)); - goto bad; - } - if ((name = elf_strptr(elf, shstrndx, shdr.sh_name)) == NULL) { - warnx("elf_strptr: %s", elf_errmsg(-1)); - goto bad; - } - if (strcmp(name, ELF_SYMTAB) == 0 && - shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { - symtab = scn; - symtab_size = shdr.sh_size / shdr.sh_entsize; - } - if (strcmp(name, ELF_STRTAB) == 0 && - shdr.sh_type == SHT_STRTAB) { - strtabndx = elf_ndxscn(scn); - } - } - if (symtab == NULL) { - warnx("%s: %s: section not found", path, ELF_SYMTAB); - goto bad; - } - if (strtabndx == SIZE_MAX) { - warnx("%s: %s: section not found", path, ELF_STRTAB); - goto bad; - } - - data = elf_rawdata(symtab, data); - if (data == NULL) + error = kelf_parse(syms); + if (error) goto bad; - if ((syms = calloc(1, sizeof(*syms))) == NULL) - err(1, NULL); - syms->table = calloc(symtab_size, sizeof *syms->table); - if (syms->table == NULL) - err(1, NULL); - for (i = 0; i < symtab_size; i++) { - if (gelf_getsym(data, i, &sym) == NULL) - continue; - if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) - continue; - name = elf_strptr(elf, strtabndx, sym.st_name); - if (name == NULL) - continue; - syms->table[syms->nsymb].sym_name = strdup(name); - if (syms->table[syms->nsymb].sym_name == NULL) - err(1, NULL); - syms->table[syms->nsymb].sym_value = sym.st_value; - syms->table[syms->nsymb].sym_size = sym.st_size; - syms->nsymb++; - } - tmp = reallocarray(syms->table, syms->nsymb, sizeof *syms->table); - if (tmp == NULL) - err(1, NULL); - syms->table = tmp; - - /* Sort symbols in ascending order by address. */ - qsort(syms->table, syms->nsymb, sizeof *syms->table, sym_compare_sort); - - /* - * Some functions, particularly those written in assembly, have an - * st_size of zero. We can approximate a size for these by assuming - * that they extend from their st_value to that of the next function. - */ - for (i = 0; i < syms->nsymb; i++) { - if (syms->table[i].sym_size != 0) - continue; - /* Can't do anything for the last symbol. */ - if (i + 1 == syms->nsymb) - continue; - diff = syms->table[i + 1].sym_value - syms->table[i].sym_value; - syms->table[i].sym_size = diff; - } + return syms; bad: - elf_end(elf); - close(fd); - return syms; + kelf_close(syms); + return NULL; } void kelf_close(struct syms *syms) { - size_t i; - if (syms == NULL) return; - - for (i = 0; i < syms->nsymb; i++) - free(syms->table[i].sym_name); - free(syms->table); + elf_end(syms->elf); + close(syms->fd); free(syms); } @@ -181,46 +91,106 @@ int kelf_snprintsym(struct syms *syms, char *str, size_t size, unsigned long pc, unsigned long off) { - struct sym key = { .sym_value = pc + off }; - struct sym *entry; - Elf_Addr offset; + GElf_Sym sym; + Elf_Data *data = NULL; + Elf_Addr offset, bestoff = 0; + size_t i, bestidx = 0; + char *name; + int cnt; if (syms == NULL) goto fallback; - entry = bsearch(&key, syms->table, syms->nsymb, sizeof *syms->table, - sym_compare_search); - if (entry == NULL) + data = elf_rawdata(syms->symtab, data); + if (data == NULL) goto fallback; - offset = pc - (entry->sym_value + off); - if (offset != 0) { - return snprintf(str, size, "\n%s+0x%llx", - entry->sym_name, (unsigned long long)offset); + for (i = 0; i < syms->nsymb; i++) { + if (gelf_getsym(data, i, &sym) == NULL) + continue; + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) + continue; + if (pc >= sym.st_value + off) { + if (pc < (sym.st_value + off + sym.st_size)) + break; + /* Workaround for symbols w/o size, usually asm ones. */ + if (sym.st_size == 0 && sym.st_value + off > bestoff) { + bestidx = i; + bestoff = sym.st_value + off; + } + } } - return snprintf(str, size, "\n%s", entry->sym_name); + if (i == syms->nsymb) { + if (bestidx == 0 || gelf_getsym(data, bestidx, &sym) == NULL) + goto fallback; + } + + name = elf_strptr(syms->elf, syms->strtabndx, sym.st_name); + if (name != NULL) + cnt = snprintf(str, size, "\n%s", name); + else + cnt = snprintf(str, size, "\n0x%llx", sym.st_value); + if (cnt < 0) + return cnt; + + offset = pc - (sym.st_value + off); + if (offset != 0) { + int l; + + l = snprintf(str + cnt, size > (size_t)cnt ? size - cnt : 0, + "+0x%llx", (unsigned long long)offset); + if (l < 0) + return l; + cnt += l; + } + + return cnt; fallback: return snprintf(str, size, "\n0x%lx", pc); } int -sym_compare_sort(const void *ap, const void *bp) +kelf_parse(struct syms *syms) { - const struct sym *a = ap, *b = bp; + GElf_Shdr shdr; + Elf_Scn *scn, *scnctf; + char *name; + size_t shstrndx; - if (a->sym_value < b->sym_value) - return -1; - return a->sym_value > b->sym_value; -} - -int -sym_compare_search(const void *keyp, const void *entryp) -{ - const struct sym *entry = entryp, *key = keyp; - - if (key->sym_value < entry->sym_value) - return -1; - return key->sym_value >= entry->sym_value + entry->sym_size; + if (elf_getshdrstrndx(syms->elf, &shstrndx) != 0) { + warnx("elf_getshdrstrndx: %s", elf_errmsg(-1)); + return 1; + } + + scn = scnctf = NULL; + while ((scn = elf_nextscn(syms->elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) != &shdr) { + warnx("elf_getshdr: %s", elf_errmsg(-1)); + return 1; + } + + if ((name = elf_strptr(syms->elf, shstrndx, + shdr.sh_name)) == NULL) { + warnx("elf_strptr: %s", elf_errmsg(-1)); + return 1; + } + + if (strcmp(name, ELF_SYMTAB) == 0 && + shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { + syms->symtab = scn; + syms->nsymb = shdr.sh_size / shdr.sh_entsize; + } + + if (strcmp(name, ELF_STRTAB) == 0 && + shdr.sh_type == SHT_STRTAB) { + syms->strtabndx = elf_ndxscn(scn); + } + } + + if (syms->symtab == NULL) + warnx("symbol table not found"); + + return 0; }