untrusted comment: verify with openbsd-79-base.pub RWTSdNN9A3yvWHFsmqKJn9eiM2dq0eeRuBXlSWaaolnVh2B3WTPffLaY4gdfQ55BBpw/3AbD6OMM6BpEYnS1/xcEsILq/t3uEA0= OpenBSD 7.9 errata 050, November 14, 2026: Fix a buffer overflow in the libc stub DNS resolver. Update the resolver code to the version in OpenBSD 8.0. Apply by doing: signify -Vep /etc/signify/openbsd-79-base.pub -x 050_libcasr.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install libc and statically linked programs: cd /usr/src/lib/libc make obj make make install cd /usr/src/bin/mt make obj make make install cd /usr/src/sbin/dump make obj make make install cd /usr/src/sbin/ifconfig make obj make make install cd /usr/src/sbin/ipsecctl make obj make make install cd /usr/src/sbin/mount_nfs make obj make make install cd /usr/src/sbin/pfctl make obj make make install cd /usr/src/sbin/ping make obj make make install cd /usr/src/sbin/restore make obj make make install cd /usr/src/sbin/route make obj make make install cd /usr/src/sbin/umount make obj make make install cd /usr/src/usr.bin/bgplg make obj make make install Index: lib/libc/asr/asr.c =================================================================== RCS file: /cvs/src/lib/libc/asr/asr.c,v diff -u -p -r1.70 asr.c --- lib/libc/asr/asr.c 10 Mar 2026 00:06:39 -0000 1.70 +++ lib/libc/asr/asr.c 8 Nov 2026 10:51:57 -0000 @@ -860,9 +860,13 @@ asr_parse_nameserver(struct sockaddr *sa char * _asr_strdname(const char *_dname, char *buf, size_t max) { - const unsigned char *dname = _dname; - char *res; - size_t left, count; + const unsigned char *dname = _dname; + unsigned char c; + char *res; + size_t left, count; + + if (max == 0) + return (buf); if (_dname[0] == 0) { strlcpy(buf, ".", max); @@ -873,10 +877,29 @@ _asr_strdname(const char *_dname, char * left = max - 1; while (dname[0] && left) { count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); - memmove(buf, dname + 1, count); - dname += dname[0] + 1; - left -= count; - buf += count; + if (left < 4 * count) { + *res = '\0'; + return res; + } + dname++; + while (count > 0) { + c = *dname++; + count--; + if ( c == '\\') { + *buf++ = '\\'; + *buf++ = '\\'; + left -= 2; + } else if (c > 0x20 && c < 0x7f) { + *buf++ = c; + left--; + } else { + *buf++ = '\\'; + *buf++ = '0' + ((c / 100) % 10); + *buf++ = '0' + ((c / 10) % 10); + *buf++ = '0' + (c % 10); + left -= 4; + } + } if (left) { left -= 1; *buf++ = '.'; Index: lib/libc/asr/asr_utils.c =================================================================== RCS file: /cvs/src/lib/libc/asr/asr_utils.c,v diff -u -p -r1.22 asr_utils.c --- lib/libc/asr/asr_utils.c 20 Nov 2023 12:15:16 -0000 1.22 +++ lib/libc/asr/asr_utils.c 8 Nov 2026 10:51:57 -0000 @@ -112,7 +112,7 @@ static ssize_t dname_expand(const unsigned char *data, size_t len, size_t offset, size_t *newoffset, char *dst, size_t max) { - size_t n, count, end, ptr, start; + size_t n, end, ptr, start; ssize_t res; if (offset >= len) @@ -140,11 +140,12 @@ dname_expand(const unsigned char *data, return (-1); /* copy n + at offset+1 */ - if (dst != NULL && max != 0) { - count = (max < n + 1) ? (max) : (n + 1); - memmove(dst, data + offset, count); - dst += count; - max -= count; + if (dst != NULL) { + if (max < n + 1) + return (-1); + memmove(dst, data + offset, n + 1); + dst += n + 1; + max -= n + 1; } res += n + 1; offset += n + 1; @@ -154,8 +155,11 @@ dname_expand(const unsigned char *data, if (end < offset + 1) end = offset + 1; - if (dst != NULL && max != 0) + if (dst != NULL) { + if (max < 1) + return (-1); dst[0] = 0; + } if (newoffset) *newoffset = end; return (res + 1); @@ -243,7 +247,7 @@ unpack_dname(struct asr_unpack *p, char p->err = EINVAL; return (-1); } - if (e < 0 || e > MAXDNAME) { + if (e < 0 || e > MAXCDNAME) { p->err = ERANGE; return (-1); } Index: lib/libc/asr/getaddrinfo_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/getaddrinfo_async.c,v diff -u -p -r1.65 getaddrinfo_async.c --- lib/libc/asr/getaddrinfo_async.c 10 Mar 2026 00:06:39 -0000 1.65 +++ lib/libc/asr/getaddrinfo_async.c 8 Nov 2026 10:51:57 -0000 @@ -43,7 +43,6 @@ struct match { static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); static int get_port(const char *, const char *, int); -static int iter_family(struct asr_query *, int); static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); static int addrinfo_from_file(struct asr_query *, int, FILE *); static int addrinfo_from_pkt(struct asr_query *, char *, size_t); @@ -123,6 +122,26 @@ getaddrinfo_async_run(struct asr_query * struct sockaddr_in sain; struct sockaddr_in6 sain6; } sa; + const struct sockaddr_in sockaddr_in_any = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + .sin_addr = htonl(INADDR_ANY) + }; + const struct sockaddr_in sockaddr_in_lo = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + .sin_addr = htonl(INADDR_LOOPBACK) + }; + const struct sockaddr_in6 sockaddr_in6_any = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = IN6ADDR_ANY_INIT + }; + const struct sockaddr_in6 sockaddr_in6_lo = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = IN6ADDR_LOOPBACK_INIT + }; next: switch (as->as_state) { @@ -231,6 +250,10 @@ getaddrinfo_async_run(struct asr_query * if (!(ai->ai_flags & AI_NUMERICHOST)) is_localhost = _asr_is_localhost(as->as.ai.hostname); + + if (as->as.ai.hostname == NULL) + is_localhost = (ai->ai_flags & AI_PASSIVE)? 0 : 1; + /* * If hostname is NULL, "localhost" or falls within the * ".localhost." domain, use local address. @@ -243,27 +266,22 @@ getaddrinfo_async_run(struct asr_query * * DNS server(s). */ if (as->as.ai.hostname == NULL || is_localhost) { - for (family = iter_family(as, 1); - family != -1; - family = iter_family(as, 0)) { - /* - * We could use statically built sockaddrs for - * those, rather than parsing over and over. - */ - if (family == PF_INET) - str = (ai->ai_flags & AI_PASSIVE && - !is_localhost) ? "0.0.0.0" : - "127.0.0.1"; - else /* PF_INET6 */ - str = (ai->ai_flags & AI_PASSIVE && - !is_localhost) ? "::" : "::1"; - /* This can't fail */ - _asr_sockaddr_from_str(&sa.sa, family, str); - if ((r = addrinfo_add(as, &sa.sa, - "localhost."))) { + if (ai->ai_family == AF_UNSPEC || + ai->ai_family == AF_INET) { + if ((r = addrinfo_add(as, + (const struct sockaddr *)((is_localhost)? + &sockaddr_in_lo : &sockaddr_in_any), + "localhost."))) + ar->ar_gai_errno = r; + } + if (ar->ar_gai_errno == 0 && + (ai->ai_family == AF_UNSPEC || + ai->ai_family == AF_INET6)) { + if ((r = addrinfo_add(as, + (const struct sockaddr *)((is_localhost)? + &sockaddr_in6_lo : &sockaddr_in6_any), + "localhost."))) ar->ar_gai_errno = r; - break; - } } if (ar->ar_gai_errno == 0 && as->as_count == 0) { ar->ar_gai_errno = EAI_NODATA; @@ -292,9 +310,9 @@ getaddrinfo_async_run(struct asr_query * /* make sure there are no funny characters in hostname */ if (!hnok_lenient(as->as.ai.hostname)) { ar->ar_gai_errno = EAI_FAIL; - async_set_state(as, ASR_STATE_HALT); - break; - } + async_set_state(as, ASR_STATE_HALT); + break; + } async_set_state(as, ASR_STATE_NEXT_DB); break; @@ -510,28 +528,6 @@ get_port(const char *servname, const cha } /* - * Iterate over the address families that are to be queried. Use the - * list on the async context, unless a specific family was given in hints. - */ -static int -iter_family(struct asr_query *as, int first) -{ - if (first) { - as->as_family_idx = 0; - if (as->as.ai.hints.ai_family != PF_UNSPEC) - return as->as.ai.hints.ai_family; - return AS_FAMILY(as); - } - - if (as->as.ai.hints.ai_family != PF_UNSPEC) - return (-1); - - as->as_family_idx++; - - return AS_FAMILY(as); -} - -/* * Use the sockaddr at "sa" to extend the result list on the "as" context, * with the specified canonical name "cname". This function adds one * entry per protocol/socktype match. @@ -651,12 +647,18 @@ addrinfo_from_pkt(struct asr_query *as, char buf[MAXDNAME], *c; _asr_unpack_init(&p, pkt, pktlen); - _asr_unpack_header(&p, &h); - for (; h.qdcount; h.qdcount--) - _asr_unpack_query(&p, &q); + if (_asr_unpack_header(&p, &h) == -1) + return (-1); + + for (; h.qdcount; h.qdcount--) { + if (_asr_unpack_query(&p, &q) == -1) + return (-1); + } for (i = 0; i < h.ancount; i++) { - _asr_unpack_rr(&p, &rr); + if (_asr_unpack_rr(&p, &rr) == -1) + return (-1); + if (rr.rr_type != q.q_type || rr.rr_class != q.q_class) continue; @@ -678,7 +680,10 @@ addrinfo_from_pkt(struct asr_query *as, if (as->as.ai.hints.ai_flags & AI_CANONNAME) { _asr_strdname(rr.rr_dname, buf, sizeof buf); buf[strlen(buf) - 1] = '\0'; - c = res_hnok(buf) ? buf : as->as.ai.hostname; + if (buf[0] != '\0' && res_hnok(buf)) + c = buf; + else + c = as->as.ai.hostname; } else if (as->as.ai.hints.ai_flags & AI_FQDN) c = as->as.ai.fqdn; else Index: lib/libc/asr/gethostnamadr_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/gethostnamadr_async.c,v diff -u -p -r1.51 gethostnamadr_async.c --- lib/libc/asr/gethostnamadr_async.c 10 Mar 2026 00:06:39 -0000 1.51 +++ lib/libc/asr/gethostnamadr_async.c 8 Nov 2026 10:51:57 -0000 @@ -55,7 +55,7 @@ struct netent_ext { static int gethostnamadr_async_run(struct asr_query *, struct asr_result *); static struct hostent_ext *hostent_alloc(int); static int hostent_set_cname(struct hostent_ext *, const char *, int); -static int hostent_add_alias(struct hostent_ext *, const char *, int); +static void hostent_add_alias(struct hostent_ext *, const char *, int); static int hostent_add_addr(struct hostent_ext *, const void *, size_t); static struct hostent_ext *hostent_from_addr(int, const char *, const char *); static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *, @@ -477,8 +477,7 @@ found: if (hostent_set_cname(h, tokens[1], 0) == -1) goto fail; for (i = 2; i < n; i ++) - if (hostent_add_alias(h, tokens[i], 0) == -1) - goto fail; + hostent_add_alias(h, tokens[i], 0); if (hostent_add_addr(h, addr, h->h.h_length) == -1) goto fail; return (h); @@ -498,30 +497,38 @@ hostent_from_packet(int reqtype, int fam struct asr_dns_header hdr; struct asr_dns_query q; struct asr_dns_rr rr; - char dname[MAXDNAME]; + char dname[MAXDNAME], rr_dname[MAXDNAME]; if ((h = hostent_alloc(family)) == NULL) return (NULL); _asr_unpack_init(&p, pkt, pktlen); - _asr_unpack_header(&p, &hdr); - for (; hdr.qdcount; hdr.qdcount--) - _asr_unpack_query(&p, &q); - strlcpy(dname, q.q_dname, sizeof(dname)); + if (_asr_unpack_header(&p, &hdr) == -1) + goto fail; + + for (; hdr.qdcount; hdr.qdcount--) { + if (_asr_unpack_query(&p, &q) == -1) + goto fail; + } + + _asr_strdname(q.q_dname, dname, sizeof(dname)); for (; hdr.ancount; hdr.ancount--) { - _asr_unpack_rr(&p, &rr); + if (_asr_unpack_rr(&p, &rr) == -1) + goto fail; + if (rr.rr_class != C_IN) continue; switch (rr.rr_type) { case T_CNAME: if (reqtype == ASR_GETHOSTBYNAME) { - if (hostent_add_alias(h, rr.rr_dname, 1) == -1) - goto fail; + hostent_add_alias(h, rr.rr_dname, 1); } else { - if (strcasecmp(rr.rr_dname, dname) == 0) - strlcpy(dname, rr.rr.cname.cname, + _asr_strdname(rr.rr_dname, rr_dname, + sizeof(rr_dname)); + if (strcasecmp(rr_dname, dname) == 0) + _asr_strdname(rr.rr.cname.cname, dname, sizeof(dname)); } break; @@ -529,7 +536,8 @@ hostent_from_packet(int reqtype, int fam case T_PTR: if (reqtype != ASR_GETHOSTBYADDR) break; - if (strcasecmp(rr.rr_dname, dname) != 0) + _asr_strdname(rr.rr_dname, rr_dname, sizeof(rr_dname)); + if (strcasecmp(rr_dname, dname) != 0) continue; if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) hostent_add_alias(h, rr.rr.ptr.ptrname, 1); @@ -597,7 +605,7 @@ hostent_set_cname(struct hostent_ext *h, if (isdname) { _asr_strdname(name, buf, sizeof buf); buf[strlen(buf) - 1] = '\0'; - if (!res_hnok(buf)) + if (buf[0] == '\0' || !res_hnok(buf)) return (-1); name = buf; } @@ -612,7 +620,7 @@ hostent_set_cname(struct hostent_ext *h, return (0); } -static int +static void hostent_add_alias(struct hostent_ext *h, const char *name, int isdname) { char buf[MAXDNAME]; @@ -622,24 +630,23 @@ hostent_add_alias(struct hostent_ext *h, if (h->aliases[i] == NULL) break; if (i == MAXALIASES) - return (0); + return; if (isdname) { _asr_strdname(name, buf, sizeof buf); buf[strlen(buf)-1] = '\0'; - if (!res_hnok(buf)) - return (-1); + if (buf[0] == '\0' || !res_hnok(buf)) + return; name = buf; } n = strlen(name) + 1; if (h->pos + n >= h->end) - return (0); + return; h->aliases[i] = h->pos; memmove(h->pos, name, n); h->pos += n; - return (0); } static int Index: lib/libc/asr/getnameinfo_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/getnameinfo_async.c,v diff -u -p -r1.15 getnameinfo_async.c --- lib/libc/asr/getnameinfo_async.c 21 Dec 2020 09:40:35 -0000 1.15 +++ lib/libc/asr/getnameinfo_async.c 8 Nov 2026 10:51:57 -0000 @@ -281,7 +281,10 @@ _numerichost(struct asr_query *as) if (ifname == NULL) snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); - strlcat(buf, scope, buflen); + if (strlcat(buf, scope, buflen) >= buflen) { + errno = ENOSPC; + return (-1); + } } return (0); Index: lib/libc/asr/getrrsetbyname.c =================================================================== RCS file: /cvs/src/lib/libc/asr/getrrsetbyname.c,v diff -u -p -r1.6 getrrsetbyname.c --- lib/libc/asr/getrrsetbyname.c 14 Sep 2015 07:38:37 -0000 1.6 +++ lib/libc/asr/getrrsetbyname.c 8 Nov 2026 10:51:57 -0000 @@ -53,31 +53,24 @@ getrrsetbyname(const char *name, unsigne void freerrset(struct rrsetinfo *rrset) { - u_int16_t i; + unsigned int i; if (rrset == NULL) return; if (rrset->rri_rdatas) { - for (i = 0; i < rrset->rri_nrdatas; i++) { - if (rrset->rri_rdatas[i].rdi_data == NULL) - break; + for (i = 0; i < rrset->rri_nrdatas; i++) free(rrset->rri_rdatas[i].rdi_data); - } free(rrset->rri_rdatas); } if (rrset->rri_sigs) { - for (i = 0; i < rrset->rri_nsigs; i++) { - if (rrset->rri_sigs[i].rdi_data == NULL) - break; + for (i = 0; i < rrset->rri_nsigs; i++) free(rrset->rri_sigs[i].rdi_data); - } free(rrset->rri_sigs); } - if (rrset->rri_name) - free(rrset->rri_name); + free(rrset->rri_name); free(rrset); } DEF_WEAK(freerrset); Index: lib/libc/asr/getrrsetbyname_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/getrrsetbyname_async.c,v diff -u -p -r1.14 getrrsetbyname_async.c --- lib/libc/asr/getrrsetbyname_async.c 7 May 2024 23:40:53 -0000 1.14 +++ lib/libc/asr/getrrsetbyname_async.c 8 Nov 2026 10:51:57 -0000 @@ -228,7 +228,7 @@ struct dns_rr { char *name; u_int16_t type; u_int16_t class; - u_int16_t ttl; + u_int32_t ttl; u_int16_t size; void *rdata; struct dns_rr *next; @@ -570,27 +570,28 @@ parse_dns_rrsection(const u_char *answer static void free_dns_query(struct dns_query *p) { - if (p == NULL) - return; + struct dns_query *next; - if (p->name) + while (p != NULL) { + next = p->next; free(p->name); - free_dns_query(p->next); - free(p); + free(p); + p = next; + } } static void free_dns_rr(struct dns_rr *p) { - if (p == NULL) - return; + struct dns_rr *next; - if (p->name) + while (p != NULL) { + next = p->next; free(p->name); - if (p->rdata) free(p->rdata); - free_dns_rr(p->next); - free(p); + free(p); + p = next; + } } static void Index: lib/libc/asr/res_search_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/res_search_async.c,v diff -u -p -r1.21 res_search_async.c --- lib/libc/asr/res_search_async.c 27 Feb 2017 10:44:46 -0000 1.21 +++ lib/libc/asr/res_search_async.c 8 Nov 2026 10:51:57 -0000 @@ -160,6 +160,8 @@ res_search_async_run(struct asr_query *a } free(ar->ar_data); + ar->ar_datalen = -1; + ar->ar_data = NULL; /* * The original resolver does something like this. Index: lib/libc/asr/res_send_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/res_send_async.c,v diff -u -p -r1.41 res_send_async.c --- lib/libc/asr/res_send_async.c 20 Jun 2022 06:45:31 -0000 1.41 +++ lib/libc/asr/res_send_async.c 8 Nov 2026 10:51:57 -0000 @@ -74,9 +74,11 @@ res_send_async(const unsigned char *buf, as->as.dns.obufsize = buflen; _asr_unpack_init(&p, buf, buflen); - _asr_unpack_header(&p, &h); - _asr_unpack_query(&p, &q); - if (p.err) { + if (_asr_unpack_header(&p, &h) == -1) { + errno = EINVAL; + goto err; + } + if (_asr_unpack_query(&p, &q) == -1) { errno = EINVAL; goto err; } @@ -308,7 +310,7 @@ sockaddr_connect(const struct sockaddr * int errno_save, sock; if ((sock = socket(sa->sa_family, - socktype | SOCK_NONBLOCK | SOCK_DNS, 0)) == -1) + socktype | SOCK_NONBLOCK | SOCK_DNS | SOCK_CLOEXEC, 0)) == -1) goto fail; if (connect(sock, sa, sa->sa_len) == -1) { @@ -355,7 +357,7 @@ setup_query(struct asr_query *as, const return (-1); } - if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { + if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) == 0) { errno = EINVAL; DPRINT("asr_make_fqdn: name too long\n"); return (-1); @@ -684,11 +686,11 @@ validate_packet(struct asr_query *as) struct asr_dns_query q; struct asr_dns_rr rr; int r; + char q_dname[MAXDNAME], as_dname[MAXDNAME]; _asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); - _asr_unpack_header(&p, &h); - if (p.err) + if (_asr_unpack_header(&p, &h) == -1) goto inval; if (h.id != as->as.dns.reqid) { @@ -710,15 +712,17 @@ validate_packet(struct asr_query *as) as->as.dns.rcode = RCODE(h.flags); as->as.dns.ancount = h.ancount; - _asr_unpack_query(&p, &q); - if (p.err) + if (_asr_unpack_query(&p, &q) == -1) goto inval; + _asr_strdname(q.q_dname, q_dname, sizeof(q_dname)); + _asr_strdname(as->as.dns.dname, as_dname, sizeof(as_dname)); + if (q.q_type != as->as.dns.type || q.q_class != as->as.dns.class || - strcasecmp(q.q_dname, as->as.dns.dname)) { + strcasecmp(q_dname, as_dname)) { DPRINT("incorrect type/class/dname '%s' != '%s'\n", - q.q_dname, as->as.dns.dname); + q_dname, as_dname); goto inval; } @@ -730,14 +734,13 @@ validate_packet(struct asr_query *as) } /* Validate the rest of the packet */ - for (r = h.ancount + h.nscount + h.arcount; r; r--) - _asr_unpack_rr(&p, &rr); - - /* Report any error found when unpacking the RRs. */ - if (p.err) { - DPRINT("unpack: %s\n", strerror(p.err)); - errno = p.err; - return (-1); + for (r = h.ancount + h.nscount + h.arcount; r; r--) { + if (_asr_unpack_rr(&p, &rr) == -1) { + /* Report any error found when unpacking the RRs. */ + DPRINT("unpack: %s\n", strerror(p.err)); + errno = p.err; + return (-1); + } } if (p.offset != as->as.dns.ibuflen) {