Version in base suite: 3.1.7-0+deb13u1 Base version: bird3_3.1.7-0+deb13u1 Target version: bird3_3.1.8-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/b/bird3/bird3_3.1.7-0+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/b/bird3/bird3_3.1.8-0+deb13u1.dsc .gitlab-ci.yml | 182 ---------- NEWS | 13 README | 4 VERSION | 2 conf/conf.c | 5 conf/flowspec.Y | 20 - debian/changelog | 6 doc/bird.sgml | 4 filter/filter.c | 2 gitlab/data.yml.j2 | 16 gitlab/docker/debian-12-amd64/Dockerfile | 4 gitlab/docker/debian-13-amd64/Dockerfile | 4 gitlab/docker/debian-testing-amd64/Dockerfile | 4 gitlab/docker/ubuntu-24.04-amd64/Dockerfile | 6 gitlab/docker/ubuntu-25.04-amd64/Dockerfile | 6 gitlab/docker/ubuntu-25.10-amd64/Dockerfile | 6 gitlab/docker/ubuntu-26.04-amd64/Dockerfile | 6 lib/flowspec.c | 445 +++++++++++++++++------- lib/flowspec.h | 15 lib/flowspec_test.c | 465 ++++++++++++++++++++++++-- lib/lockfree.c | 35 + lib/mempool.c | 6 lib/obstacle.h | 10 lib/rcu.h | 17 lib/resource.c | 4 lib/resource.h | 24 + lib/slab.c | 8 lib/string.h | 8 nest/iface.c | 3 nest/password.h | 4 nest/proto.c | 2 nest/rt-table.c | 9 proto/bfd/packets.c | 7 proto/bgp/bgp.c | 11 proto/bgp/packets.c | 18 - proto/l3vpn/l3vpn.c | 1 proto/ospf/packet.c | 7 proto/pipe/pipe.c | 1 proto/rip/packets.c | 6 sysdep/unix/alloc.c | 17 sysdep/unix/io-loop.c | 2 sysdep/unix/log.c | 30 - 42 files changed, 990 insertions(+), 455 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpv5jezq4m/bird3_3.1.7-0+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpv5jezq4m/bird3_3.1.8-0+deb13u1.dsc: no acceptable signature found diff -Nru bird3-3.1.7/.gitlab-ci.yml bird3-3.1.8/.gitlab-ci.yml --- bird3-3.1.7/.gitlab-ci.yml 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/.gitlab-ci.yml 2026-07-30 15:32:45.000000000 +0000 @@ -1133,15 +1133,6 @@ variables: CROSSBUILD_ARCH: mips64el -crossbuild-s390x-debian-12-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-debian-12-amd64 - optional: true - image: registry.nic.cz/labs/bird:debian-12-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-debian-12-i386: <<: *build-docker-linux-amd64 @@ -1193,15 +1184,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-debian-13-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-debian-13-amd64 - optional: true - image: registry.nic.cz/labs/bird:debian-13-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-debian-13-i386: <<: *build-docker-linux-amd64 @@ -1244,15 +1226,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-debian-testing-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-debian-testing-amd64 - optional: true - image: registry.nic.cz/labs/bird:debian-testing-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-debian-testing-i386: <<: *build-docker-linux-amd64 @@ -1328,15 +1301,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-ubuntu-24.04-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-ubuntu-24.04-amd64 - optional: true - image: registry.nic.cz/labs/bird:ubuntu-24.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-ubuntu-25.04-amd64: <<: *build-docker-linux-amd64 @@ -1372,15 +1336,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-ubuntu-25.04-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-ubuntu-25.04-amd64 - optional: true - image: registry.nic.cz/labs/bird:ubuntu-25.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-ubuntu-25.10-amd64: <<: *build-docker-linux-amd64 @@ -1425,15 +1380,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-ubuntu-25.10-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-ubuntu-25.10-amd64 - optional: true - image: registry.nic.cz/labs/bird:ubuntu-25.10-amd64 - variables: - CROSSBUILD_ARCH: s390x - build-ubuntu-26.04-amd64: <<: *build-docker-linux-amd64 @@ -1478,15 +1424,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crossbuild-s390x-ubuntu-26.04-amd64: - <<: *build-docker-linux-amd64 - needs: - - job: docker-ubuntu-26.04-amd64 - optional: true - image: registry.nic.cz/labs/bird:ubuntu-26.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - ####################################################### @@ -1804,16 +1741,6 @@ variables: CROSSBUILD_ARCH: mips64el -crosspkg-s390x-debian-12-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-debian-12-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:debian-12-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-debian-12-i386: <<: *pkg-deb @@ -1871,16 +1798,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-debian-13-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-debian-13-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:debian-13-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-debian-13-i386: <<: *pkg-deb @@ -1928,16 +1845,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-debian-testing-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-debian-testing-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:debian-testing-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-debian-testing-i386: <<: *pkg-deb @@ -2022,16 +1929,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-ubuntu-24.04-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-ubuntu-24.04-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:ubuntu-24.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-ubuntu-25.04-amd64: <<: *pkg-deb @@ -2071,16 +1968,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-ubuntu-25.04-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-ubuntu-25.04-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:ubuntu-25.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-ubuntu-25.10-amd64: <<: *pkg-deb @@ -2130,16 +2017,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-ubuntu-25.10-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-ubuntu-25.10-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:ubuntu-25.10-amd64 - variables: - CROSSBUILD_ARCH: s390x - pkg-ubuntu-26.04-amd64: <<: *pkg-deb @@ -2189,16 +2066,6 @@ variables: CROSSBUILD_ARCH: riscv64 -crosspkg-s390x-ubuntu-26.04-amd64: - <<: *pkg-deb - needs: - - job: crossbuild-s390x-ubuntu-26.04-amd64 - artifacts: false - - job: build-release - image: registry.nic.cz/labs/bird:ubuntu-26.04-amd64 - variables: - CROSSBUILD_ARCH: s390x - ## Package installability checks @@ -2492,13 +2359,6 @@ image: registry.nic.cz/labs/bird:debian-12-amd64 variables: CROSSBUILD_ARCH: mips64el -crossinstall-s390x-debian-12-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-debian-12-amd64 - image: registry.nic.cz/labs/bird:debian-12-amd64 - variables: - CROSSBUILD_ARCH: s390x install-debian-12-i386: <<: *install-deb @@ -2541,13 +2401,6 @@ image: registry.nic.cz/labs/bird:debian-13-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-debian-13-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-debian-13-amd64 - image: registry.nic.cz/labs/bird:debian-13-amd64 - variables: - CROSSBUILD_ARCH: s390x install-debian-13-i386: <<: *install-deb @@ -2583,13 +2436,6 @@ image: registry.nic.cz/labs/bird:debian-testing-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-debian-testing-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-debian-testing-amd64 - image: registry.nic.cz/labs/bird:debian-testing-amd64 - variables: - CROSSBUILD_ARCH: s390x install-debian-testing-i386: <<: *install-deb @@ -2653,13 +2499,6 @@ image: registry.nic.cz/labs/bird:ubuntu-24.04-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-ubuntu-24.04-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-ubuntu-24.04-amd64 - image: registry.nic.cz/labs/bird:ubuntu-24.04-amd64 - variables: - CROSSBUILD_ARCH: s390x install-ubuntu-25.04-amd64: <<: *install-deb @@ -2688,13 +2527,6 @@ image: registry.nic.cz/labs/bird:ubuntu-25.04-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-ubuntu-25.04-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-ubuntu-25.04-amd64 - image: registry.nic.cz/labs/bird:ubuntu-25.04-amd64 - variables: - CROSSBUILD_ARCH: s390x install-ubuntu-25.10-amd64: <<: *install-deb @@ -2730,13 +2562,6 @@ image: registry.nic.cz/labs/bird:ubuntu-25.10-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-ubuntu-25.10-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-ubuntu-25.10-amd64 - image: registry.nic.cz/labs/bird:ubuntu-25.10-amd64 - variables: - CROSSBUILD_ARCH: s390x install-ubuntu-26.04-amd64: <<: *install-deb @@ -2772,13 +2597,6 @@ image: registry.nic.cz/labs/bird:ubuntu-26.04-amd64 variables: CROSSBUILD_ARCH: riscv64 -crossinstall-s390x-ubuntu-26.04-amd64: - <<: *install-deb - needs: - - job: crosspkg-s390x-ubuntu-26.04-amd64 - image: registry.nic.cz/labs/bird:ubuntu-26.04-amd64 - variables: - CROSSBUILD_ARCH: s390x ################################### diff -Nru bird3-3.1.7/NEWS bird3-3.1.8/NEWS --- bird3-3.1.7/NEWS 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/NEWS 2026-07-30 15:32:45.000000000 +0000 @@ -1,3 +1,16 @@ +Version 3.1.8 (2026-07-30) + o BGP: Fix stack buffer overflow in Flowspec NLRI decoder + o BGP: Minor improvements in Flowspec parsing + o BGP: Fix minor issues with send hold timer + o Fix null byte handling in authentication keys + o Pipe, L3VPN: Fix hostentry stripping + o Filter: Fix zero arg handling + o Logging: Fix use-after-free on failed rotation + o CLI: Fix crashes in show route + o Allocator: Pre-fill hot pages when entering RCU critical section + o Fix obstacle cleanup + o Update bird-users mailing list links + Version 3.1.7 (2026-06-09) o BGP: Fix parsing labelled NLRIs with no next hop o BGP: Fix cork behavior in collision with graceful restart diff -Nru bird3-3.1.7/README bird3-3.1.8/README --- bird3-3.1.7/README 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/README 2026-07-30 15:32:45.000000000 +0000 @@ -71,8 +71,8 @@ users mailing list bird-users@network.cz, just send `subscribe' to bird-request@network.cz. -Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/ -Archive: http://bird.network.cz/pipermail/bird-users/ +Subscribe: https://bird.nic.cz/lists/bird-users/ +Archive: https://bird.nic.cz/lists/bird-users/archive/ Please don't send security issues to the mailing-list, contact us instead at bird-support@network.cz which is a private e-mail address where you also can diff -Nru bird3-3.1.7/VERSION bird3-3.1.8/VERSION --- bird3-3.1.7/VERSION 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/VERSION 2026-07-30 15:32:45.000000000 +0000 @@ -1 +1 @@ -3.1.7 +3.1.8 diff -Nru bird3-3.1.7/conf/conf.c bird3-3.1.8/conf/conf.c --- bird3-3.1.7/conf/conf.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/conf/conf.c 2026-07-30 15:32:45.000000000 +0000 @@ -81,10 +81,11 @@ /* Note that both shutting_down and undo_available are related to requests, not processing */ static void -config_obstacles_cleared(struct callback *_ UNUSED) +config_obstacles_cleared(struct callback *cb UNUSED) { ASSERT_DIE(birdloop_inside(&main_birdloop)); ASSERT_DIE(configuring); + config_done(); } @@ -206,7 +207,9 @@ return; synchronize_rcu(); + ASSERT_DIE(!obstacle_target_count(&c->obstacles)); + obstacle_target_cleanup(&c->obstacles); rp_free(c->pool); } diff -Nru bird3-3.1.7/conf/flowspec.Y bird3-3.1.8/conf/flowspec.Y --- bird3-3.1.7/conf/flowspec.Y 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/conf/flowspec.Y 2026-07-30 15:32:45.000000000 +0000 @@ -59,7 +59,7 @@ | ICMP CODE { $$ = FLOW_TYPE_ICMP_CODE; } | LENGTH { $$ = FLOW_TYPE_PACKET_LENGTH; } | DSCP { $$ = FLOW_TYPE_DSCP; } - | LABEL { $$ = FLOW_TYPE_LABEL; } + | LABEL { $$ = FLOW_TYPE_LABEL; if (!this_flow->ipv6) cf_error("Flow label not valid in flow4"); } ; flow_num_type: flow_num_type_{ flow_builder_set_type(this_flow, $1); }; @@ -73,11 +73,11 @@ flow_num_opts: flow_num_op expr { - flow_check_cf_value_length(this_flow, $2); + flow_check_cf_numeric_arg(this_flow, $2); flow_builder_add_op_val(this_flow, $1, $2); } | flow_num_opts flow_logic_op flow_num_op expr { - flow_check_cf_value_length(this_flow, $4); + flow_check_cf_numeric_arg(this_flow, $4); flow_builder_add_op_val(this_flow, $2 | $3, $4); } | flow_num_opt_ext @@ -86,12 +86,12 @@ flow_num_opt_ext_expr: expr { - flow_check_cf_value_length(this_flow, $1); + flow_check_cf_numeric_arg(this_flow, $1); flow_builder_add_op_val(this_flow, FLOW_OP_EQ, $1); } | expr DDOT expr { - flow_check_cf_value_length(this_flow, $1); - flow_check_cf_value_length(this_flow, $3); + flow_check_cf_numeric_arg(this_flow, $1); + flow_check_cf_numeric_arg(this_flow, $3); flow_builder_add_op_val(this_flow, FLOW_OP_GEQ, $1); flow_builder_add_op_val(this_flow, FLOW_OP_AND | FLOW_OP_LEQ, $3); } @@ -104,15 +104,15 @@ flow_bmk_opts: flow_neg expr '/' expr { - flow_check_cf_bmk_values(this_flow, $1, $2, $4); + flow_check_cf_bitmask_arg(this_flow, $1, $2, $4); flow_builder_add_val_mask(this_flow, $1, $2, $4); } | flow_bmk_opts flow_logic_op flow_neg expr '/' expr { - flow_check_cf_bmk_values(this_flow, $3, $4, $6); + flow_check_cf_bitmask_arg(this_flow, $3, $4, $6); flow_builder_add_val_mask(this_flow, $2 | $3, $4, $6); } | flow_bmk_opts ',' flow_neg expr '/' expr { - flow_check_cf_bmk_values(this_flow, $3, $4, $6); + flow_check_cf_bitmask_arg(this_flow, $3, $4, $6); flow_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */ } ; @@ -123,7 +123,7 @@ ; flow_frag_val: - DONT_FRAGMENT { $$ = 1; } + DONT_FRAGMENT { $$ = 1; if (this_flow->ipv6) cf_error("Flag dont_fragment not valid in flow6"); } | IS_FRAGMENT { $$ = 2; } | FIRST_FRAGMENT { $$ = 4; } | LAST_FRAGMENT { $$ = 8; } diff -Nru bird3-3.1.7/debian/changelog bird3-3.1.8/debian/changelog --- bird3-3.1.7/debian/changelog 2026-06-14 18:50:51.000000000 +0000 +++ bird3-3.1.8/debian/changelog 2026-08-02 02:55:28.000000000 +0000 @@ -1,3 +1,9 @@ +bird3 (3.1.8-0+deb13u1) stable; urgency=medium + + * New upstream release. + + -- Marco d'Itri Sun, 02 Aug 2026 04:55:28 +0200 + bird3 (3.1.7-0+deb13u1) trixie-security; urgency=medium * New upstream release. diff -Nru bird3-3.1.7/doc/bird.sgml bird3-3.1.8/doc/bird.sgml --- bird3-3.1.7/doc/bird.sgml 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/doc/bird.sgml 2026-07-30 15:32:45.000000000 +0000 @@ -6616,7 +6616,6 @@

BIRD is a relatively young system and it probably contains some bugs. You can diff -Nru bird3-3.1.7/filter/filter.c bird3-3.1.8/filter/filter.c --- bird3-3.1.7/filter/filter.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/filter/filter.c 2026-07-30 15:32:45.000000000 +0000 @@ -131,7 +131,7 @@ /* Set the arguments and top-level variables */ fstk->vcnt = line->vars + line->args; - memcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args); + bmemcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args); memset(fstk->vstk + argc, 0, sizeof(struct f_val) * line->vars); /* The same as with the value stack. Not resetting the stack completely for performance reasons. */ diff -Nru bird3-3.1.7/gitlab/data.yml.j2 bird3-3.1.8/gitlab/data.yml.j2 --- bird3-3.1.7/gitlab/data.yml.j2 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/data.yml.j2 2026-07-30 15:32:45.000000000 +0000 @@ -104,9 +104,9 @@ # Debian from oldoldstable up to testing {% for num, codename, crossarch in ( (11, "bullseye", []), - (12, "bookworm", ["mips64el", "s390x"]), - (13, "trixie", ["arm64", "armel", "armhf", "riscv64", "s390x"]), - ("testing", "testing", ["arm64", "armhf", "riscv64", "s390x"]) + (12, "bookworm", ["mips64el"]), + (13, "trixie", ["arm64", "armel", "armhf", "riscv64"]), + ("testing", "testing", ["arm64", "armhf", "riscv64"]) ) -%} {% for arch in ('amd64', 'i386') -%} - name: debian-{{num}}-{{arch}} @@ -124,16 +124,17 @@ # These debian don't crossbuild. TODO fix that. # 12 -> ["arm64", "armel", "armhf"] -> Error 127 from `make check` probably because qemu-user is somehow botched +# 12, 13, testing -> s390x -> does not install, impossible to replicate locally # Last 7 years of Ubuntu LTS {% for num, crossarch in ( ('18.04', []), ('20.04', []), ('22.04', []), - ('24.04', ["arm64", "armhf", "ppc64el", "riscv64", "s390x"]), - ('25.04', ["armhf", "ppc64el", "riscv64", "s390x"]), - ('25.10', ["arm64", "armhf", "ppc64el", "riscv64", "s390x"]), - ('26.04', ["arm64", "armhf", "ppc64el", "riscv64", "s390x"]), + ('24.04', ["arm64", "armhf", "ppc64el", "riscv64"]), + ('25.04', ["armhf", "ppc64el", "riscv64"]), + ('25.10', ["arm64", "armhf", "ppc64el", "riscv64"]), + ('26.04', ["arm64", "armhf", "ppc64el", "riscv64"]), ) -%} - name: ubuntu-{{ num }}-amd64 type: deb @@ -150,6 +151,7 @@ # -> ["s390x"] doesn't pack -> debugedit --build-id --build-id-seed=bird2/2.18\+branch.mq.nightly.d0b6b85ebc17-cznic.1\~jammy debian/bird2/usr/sbin/birdc died with signal 11 # -> ["riscv64"] fails on dynamic linking libatomic.so.1 when running tests # '25.04' -> ["arm64"] fails in `make check` with Timeout expired on `rt-fib_test` +# 24.04 through 26.04 -> s390x -> does not install, impossible to replicate locally birdlab: - name: openbsd-76 diff -Nru bird3-3.1.7/gitlab/docker/debian-12-amd64/Dockerfile bird3-3.1.8/gitlab/docker/debian-12-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/debian-12-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/debian-12-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -7,7 +7,6 @@ ENV DEBIAN_FRONTEND=noninteractive RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN dpkg --add-architecture mips64el -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -22,9 +21,6 @@ crossbuild-essential-mips64el \ libreadline-dev:mips64el \ ncurses-dev:mips64el \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/debian-13-amd64/Dockerfile bird3-3.1.8/gitlab/docker/debian-13-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/debian-13-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/debian-13-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -10,7 +10,6 @@ RUN dpkg --add-architecture armel RUN dpkg --add-architecture armhf RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -35,9 +34,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/debian-testing-amd64/Dockerfile bird3-3.1.8/gitlab/docker/debian-testing-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/debian-testing-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/debian-testing-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -9,7 +9,6 @@ RUN dpkg --add-architecture arm64 RUN dpkg --add-architecture armhf RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -30,9 +29,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/ubuntu-24.04-amd64/Dockerfile bird3-3.1.8/gitlab/docker/ubuntu-24.04-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/ubuntu-24.04-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/ubuntu-24.04-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -10,10 +10,9 @@ RUN dpkg --add-architecture armhf RUN dpkg --add-architecture ppc64el RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec sed -i '/Components: /a\ Architectures: amd64' '{}' + -RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64,s390x#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources +RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -38,9 +37,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/ubuntu-25.04-amd64/Dockerfile bird3-3.1.8/gitlab/docker/ubuntu-25.04-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/ubuntu-25.04-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/ubuntu-25.04-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -9,10 +9,9 @@ RUN dpkg --add-architecture armhf RUN dpkg --add-architecture ppc64el RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec sed -i '/Components: /a\ Architectures: amd64' '{}' + -RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: armhf,ppc64el,riscv64,s390x#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources +RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: armhf,ppc64el,riscv64#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -34,9 +33,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/ubuntu-25.10-amd64/Dockerfile bird3-3.1.8/gitlab/docker/ubuntu-25.10-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/ubuntu-25.10-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/ubuntu-25.10-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -10,10 +10,9 @@ RUN dpkg --add-architecture armhf RUN dpkg --add-architecture ppc64el RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec sed -i '/Components: /a\ Architectures: amd64' '{}' + -RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64,s390x#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources +RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -38,9 +37,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/gitlab/docker/ubuntu-26.04-amd64/Dockerfile bird3-3.1.8/gitlab/docker/ubuntu-26.04-amd64/Dockerfile --- bird3-3.1.7/gitlab/docker/ubuntu-26.04-amd64/Dockerfile 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/gitlab/docker/ubuntu-26.04-amd64/Dockerfile 2026-07-30 15:32:45.000000000 +0000 @@ -10,10 +10,9 @@ RUN dpkg --add-architecture armhf RUN dpkg --add-architecture ppc64el RUN dpkg --add-architecture riscv64 -RUN dpkg --add-architecture s390x RUN find /etc/apt/sourc* -type f -exec sed -i '/Components: /a\ Architectures: amd64' '{}' + -RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64,s390x#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources +RUN find /etc/apt/sourc* -type f -exec sed 's#URIs: .*#URIs: http://ports.ubuntu.com/ubuntu-ports/#;s#Architectures: amd64#Architectures: arm64,armhf,ppc64el,riscv64#' '{}' + > /etc/apt/sources.list.d/crossbuild.sources RUN find /etc/apt/sourc* -type f -exec grep -nH '.*' '{}' + RUN apt-get -y update RUN apt-get -y upgrade @@ -38,9 +37,6 @@ crossbuild-essential-riscv64 \ libreadline-dev:riscv64 \ ncurses-dev:riscv64 \ - crossbuild-essential-s390x \ - libreadline-dev:s390x \ - ncurses-dev:s390x \ qemu-user \ qemu-user-binfmt \ binfmt-support \ diff -Nru bird3-3.1.7/lib/flowspec.c bird3-3.1.8/lib/flowspec.c --- bird3-3.1.7/lib/flowspec.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/flowspec.c 2026-07-30 15:32:45.000000000 +0000 @@ -170,16 +170,8 @@ case FLOW_TYPE_SRC_PREFIX: { uint pxlen = *pos++; - uint bytes = BYTES(pxlen); - if (ipv6) - { - uint offset = *pos++ / 8; - pos += bytes - offset; - } - else - { - pos += bytes; - } + uint offset = ipv6 ? *pos++ : 0; + pos += BYTES(pxlen - offset); break; } @@ -332,11 +324,11 @@ [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET] = "Exceed maximal prefix offset", [FLOW_ST_EXCEED_MAX_VALUE_LENGTH] = "Exceed maximal value length", [FLOW_ST_BAD_TYPE_ORDER] = "Bad component order", - [FLOW_ST_AND_BIT_SHOULD_BE_UNSET] = "The AND-bit should be unset", - [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED] = "The Zero-bit should be unset", - [FLOW_ST_DEST_PREFIX_REQUIRED] = "Destination prefix is missing", - [FLOW_ST_INVALID_TCP_FLAGS] = "TCP flags exceeding 0xfff", - [FLOW_ST_CANNOT_USE_DONT_FRAGMENT] = "Cannot use Don't fragment flag in IPv6 flow" + [FLOW_ST_NONZERO_PADDING] = "Nonzero prefix padding", + [FLOW_ST_FIRST_AND_BIT_SET] = "The first AND-bit is set", + [FLOW_ST_ZERO_BIT_SET] = "Zero-bit is set", + [FLOW_ST_INVALID_TCP_FLAGS] = "Invalid TCP flags bitmask operand", + [FLOW_ST_INVALID_FRAGMENT] = "Invalid fragment bitmask operand", }; /** @@ -351,45 +343,28 @@ return flow_validated_state_str_[code]; } -static const u8 flow4_max_value_length[] = { - [FLOW_TYPE_DST_PREFIX] = 0, - [FLOW_TYPE_SRC_PREFIX] = 0, - [FLOW_TYPE_IP_PROTOCOL] = 1, - [FLOW_TYPE_PORT] = 2, - [FLOW_TYPE_DST_PORT] = 2, - [FLOW_TYPE_SRC_PORT] = 2, - [FLOW_TYPE_ICMP_TYPE] = 1, - [FLOW_TYPE_ICMP_CODE] = 1, +/* Maximum length of value fields, as mandated by RFC 8955 */ +static const u8 flow_max_value_length[FLOW_TYPE_MAX] = { [FLOW_TYPE_TCP_FLAGS] = 2, - [FLOW_TYPE_PACKET_LENGTH] = 2, [FLOW_TYPE_DSCP] = 1, - [FLOW_TYPE_FRAGMENT] = 1 /* XXX */ + [FLOW_TYPE_FRAGMENT] = 1, }; -static const u8 flow6_max_value_length[] = { - [FLOW_TYPE_DST_PREFIX] = 0, - [FLOW_TYPE_SRC_PREFIX] = 0, - [FLOW_TYPE_NEXT_HEADER] = 1, - [FLOW_TYPE_PORT] = 2, - [FLOW_TYPE_DST_PORT] = 2, - [FLOW_TYPE_SRC_PORT] = 2, - [FLOW_TYPE_ICMP_TYPE] = 1, - [FLOW_TYPE_ICMP_CODE] = 1, - [FLOW_TYPE_TCP_FLAGS] = 2, - [FLOW_TYPE_PACKET_LENGTH] = 2, - [FLOW_TYPE_DSCP] = 1, - [FLOW_TYPE_FRAGMENT] = 1, /* XXX */ - [FLOW_TYPE_LABEL] = 4 +/* Maximum valid numeric values (in bits), semantically */ +static const u8 flow_max_valid_value[FLOW_TYPE_MAX] = { + [FLOW_TYPE_IP_PROTOCOL] = 8, + [FLOW_TYPE_PORT] = 16, + [FLOW_TYPE_DST_PORT] = 16, + [FLOW_TYPE_SRC_PORT] = 16, + [FLOW_TYPE_ICMP_TYPE] = 8, + [FLOW_TYPE_ICMP_CODE] = 8, + [FLOW_TYPE_PACKET_LENGTH] = 16, + [FLOW_TYPE_DSCP] = 6, + [FLOW_TYPE_LABEL] = 20, }; -static u8 -flow_max_value_length(enum flow_type type, int ipv6) -{ - return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type]; -} - /** - * flow_check_cf_bmk_values - check value/bitmask part of flowspec component + * flow_check_cf_bitmask_arg - check value/bitmask part of flowspec component * @fb: flow builder instance * @neg: negation operand * @val: value from value/mask pair @@ -400,61 +375,63 @@ * to failing of validation. */ void -flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask) +flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u32 mask) { - flow_check_cf_value_length(fb, val); - flow_check_cf_value_length(fb, mask); - if (neg && !(val == 0 || val == mask)) cf_error("For negation, value must be zero or bitmask"); - if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000)) + if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & ~0xfff)) cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask); - if ((fb->this_type == FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x01)) - cf_error("Invalid mask 0x%x, bit 0 must be 0", mask); + u32 valid = fb->ipv6 ? 0x0e : 0x0f; + if ((fb->this_type == FLOW_TYPE_FRAGMENT) && (mask & ~valid)) + cf_error("Invalid mask 0x%x, must not exceed 0x%x", mask, valid); if (val & ~mask) cf_error("Value 0x%x outside bitmask 0x%x", val, mask); } /** - * flow_check_cf_value_length - check value by flowspec component type + * flow_check_cf_numeric_arg - check numeric argument of flowspec component * @fb: flow builder instance * @val: value * - * This function checks if the value is in range of component's type support. - * If some problem will appear, the function calls cf_error() function with - * a textual description of reason to failing of validation. + * This function checks the value of numeric argument to see whether it is in + * the range of component's type. If some problem will appear, the function + * calls cf_error() function with a textual description of reason to failing of + * validation. */ void -flow_check_cf_value_length(struct flow_builder *fb, u32 val) +flow_check_cf_numeric_arg(struct flow_builder *fb, uint val) { enum flow_type t = fb->this_type; - u8 max = flow_max_value_length(t, fb->ipv6); + u64 max = (U64(1) << flow_max_valid_value[t]) - 1; - if (t == FLOW_TYPE_DSCP && val > 0x3f) - cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val); - - if (max == 1 && (val > 0xff)) - cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6), val); + if (max && (val > max)) + cf_error("%s value %u out of range (0-%lu)", flow_type_str(t, fb->ipv6), val, max); +} - if (max == 2 && (val > 0xffff)) - cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val); +/* Bitmask of padding bits in last byte of prefix */ +static inline u8 +flow_padding(uint n) +{ + ASSUME(n % 8 != 0); + return (1 << (8 - n % 8)) - 1; } static enum flow_validated_state -flow_validate(const byte *nlri, uint len, int ipv6) +flow_decode(byte *nlri, uint length, bool ipv6) { enum flow_type type = 0; - const byte *pos = nlri; - const byte *end = nlri + len; + byte *pos = nlri; + byte *end = nlri + length; while (pos < end) { /* Check increasing type ordering */ if (*pos <= type) return FLOW_ST_BAD_TYPE_ORDER; + type = *pos++; switch (type) @@ -462,19 +439,42 @@ case FLOW_TYPE_DST_PREFIX: case FLOW_TYPE_SRC_PREFIX: { - uint pxlen = *pos++; - if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH)) - return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH; + uint pxlen, offset = 0; - uint bytes = BYTES(pxlen); - if (ipv6) + if (!ipv6) { - uint pxoffset = *pos++; - if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen) - return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET; - bytes = BYTES(pxlen - pxoffset); + if (pos + 1 > end) + return FLOW_ST_NOT_COMPLETE; + + pxlen = *pos++; + + if (pxlen > IP4_MAX_PREFIX_LENGTH) + return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH; } - pos += bytes; + else + { + if (pos + 2 > end) + return FLOW_ST_NOT_COMPLETE; + + pxlen = *pos++; + offset = *pos++; + + if (pxlen > IP6_MAX_PREFIX_LENGTH) + return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH; + + if (offset > pxlen) + return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET; + } + + uint bits = pxlen - offset; + pos += BYTES(bits); + + if (pos > end) + return FLOW_ST_NOT_COMPLETE; + + /* Padding bits in the last byte MUST be 0 */ + if (bits % 8) + pos[-1] &= ~flow_padding(bits); break; } @@ -483,6 +483,7 @@ if (!ipv6) return FLOW_ST_UNKNOWN_COMPONENT; /* fall through */ + case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */ case FLOW_TYPE_PORT: case FLOW_TYPE_DST_PORT: @@ -494,70 +495,236 @@ case FLOW_TYPE_DSCP: case FLOW_TYPE_FRAGMENT: { - uint last = 0; - uint first = 1; + /* + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ + * | e | a | len | 0 |lt |gt |eq | | e | a | len | 0 | 0 |not| m | + * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ + * + * Numeric operator Bitmask operator + */ + bool first = true; + bool last = false; while (!last) { - /* - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | e | a | len | 0 |lt |gt |eq | - * +---+---+---+---+---+---+---+---+ - * - * Numeric operator - */ + if (pos + 1 > end) + return FLOW_ST_NOT_COMPLETE; - last = isset_end(pos); + uint len = get_value_length(pos); - /* The AND bit should in the first operator byte of a sequence */ - if (first && isset_and(pos)) - return FLOW_ST_AND_BIT_SHOULD_BE_UNSET; + /* Some component values MUST be encoded with limited length */ + uint maxlen = flow_max_value_length[type]; + if (maxlen && (len > maxlen)) + return FLOW_ST_EXCEED_MAX_VALUE_LENGTH; - /* This bit should be zero */ - if (*pos & 0x08) - return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED; + if (pos + 1 + len > end) + return FLOW_ST_NOT_COMPLETE; - if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT) - { - /* - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | e | a | len | 0 | 0 |not| m | - * +---+---+---+---+---+---+---+---+ - * - * Bitmask operand - */ - if (*pos & 0x04) - return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED; - } + /* In first operator of sequence, AND bit ... MUST be ignored during decoding */ + if (first) + pos[0] &= ~0x40; - /* Value length of operator */ - uint len = get_value_length(pos); - if (len > flow_max_value_length(type, ipv6)) - return FLOW_ST_EXCEED_MAX_VALUE_LENGTH; + /* Zero field MUST be set to 0 on encoding and MUST be ignored during decoding */ + pos[0] &= ~0x08; - /* TCP Flags component must not check highest nibble (just 12 valid bits) */ - if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2) && (pos[1] & 0xf0)) - return FLOW_ST_INVALID_TCP_FLAGS; - - /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */ - if ((type == FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01)) - return FLOW_ST_CANNOT_USE_DONT_FRAGMENT; - /* XXX: Could be a fragment component encoded in 2-bytes? */ + /* Bitmask operator has additional zero field */ + if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT) + pos[0] &= ~0x04; + /* Zero fields in highest nibble of TCP Flags bitmask operand */ + if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2)) + pos[1] &= ~0xf0; + + /* Zero fields in fragment bitmask operand */ + if ((type == FLOW_TYPE_FRAGMENT) && (len == 1)) + pos[1] &= ipv6 ? ~0xf1 : ~0xf0; + + /* Move to next operator */ + first = false; + last = isset_end(pos); pos += 1+len; + } + break; + } + + default: + return FLOW_ST_UNKNOWN_COMPONENT; + } + } + + if (pos != end) + return FLOW_ST_NOT_COMPLETE; + + return FLOW_ST_VALID; +} + +/** + * flow4_decode - decode incoming BGP IPv4 flowspec data stream + * @nlri: flowspec data stream without length header + * @len: length of @nlri + * + * This function checks syntatic correctness of binary flowspec. It returns + * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed. + * If the NLRI is valid but not normalized (e.g. some zero bits are set), the + * flow4_decode() modify the datastream to normalize it. + */ +inline enum flow_validated_state +flow4_decode(byte *nlri, uint len) +{ + return flow_decode(nlri, len, false); +} - if (pos > end && !last) +/** + * flow6_decode - decode incoming BGP IPv6 flowspec data stream + * @nlri: flowspec data stream without length header + * @len: length of @nlri + * + * This function checks syntatic correctness of binary flowspec. It returns + * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed. + * If the NLRI is valid but not normalized (e.g. some zero bits are set), the + * flow6_decode() modify the datastream to normalize it. + */ +inline enum flow_validated_state +flow6_decode(byte *nlri, uint len) +{ + return flow_decode(nlri, len, true); +} + +static enum flow_validated_state +flow_validate(const byte *nlri, uint len, bool ipv6) +{ + enum flow_type type = 0; + const byte *pos = nlri; + const byte *end = nlri + len; + + while (pos < end) + { + /* Check increasing type ordering */ + if (*pos <= type) + return FLOW_ST_BAD_TYPE_ORDER; + + type = *pos++; + + switch (type) + { + case FLOW_TYPE_DST_PREFIX: + case FLOW_TYPE_SRC_PREFIX: + { + uint pxlen, offset = 0; + + if (!ipv6) + { + if (pos + 1 > end) + return FLOW_ST_NOT_COMPLETE; + + pxlen = *pos++; + + if (pxlen > IP4_MAX_PREFIX_LENGTH) + return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH; + } + else + { + if (pos + 2 > end) + return FLOW_ST_NOT_COMPLETE; + + pxlen = *pos++; + offset = *pos++; + + if (pxlen > IP6_MAX_PREFIX_LENGTH) + return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH; + + if (offset > pxlen) + return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET; + } + + uint bits = pxlen - offset; + pos += BYTES(bits); + + if (pos > end) + return FLOW_ST_NOT_COMPLETE; + + /* Padding bits in the last byte MUST be 0 */ + if (bits % 8) + if (pos[-1] & flow_padding(bits)) + return FLOW_ST_NONZERO_PADDING; + + break; + } + + case FLOW_TYPE_LABEL: + if (!ipv6) + return FLOW_ST_UNKNOWN_COMPONENT; + /* fall through */ + + case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */ + case FLOW_TYPE_PORT: + case FLOW_TYPE_DST_PORT: + case FLOW_TYPE_SRC_PORT: + case FLOW_TYPE_ICMP_TYPE: + case FLOW_TYPE_ICMP_CODE: + case FLOW_TYPE_TCP_FLAGS: + case FLOW_TYPE_PACKET_LENGTH: + case FLOW_TYPE_DSCP: + case FLOW_TYPE_FRAGMENT: + { + /* + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ + * | e | a | len | 0 |lt |gt |eq | | e | a | len | 0 | 0 |not| m | + * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ + * + * Numeric operator Bitmask operator + */ + + bool first = true; + bool last = false; + while (!last) + { + if (pos + 1 > end) return FLOW_ST_NOT_COMPLETE; - if (pos > (end+1)) + uint len = get_value_length(pos); + + /* Some component values MUST be encoded with limited length */ + uint maxlen = flow_max_value_length[type]; + if (maxlen && (len > maxlen)) + return FLOW_ST_EXCEED_MAX_VALUE_LENGTH; + + if (pos + 1 + len > end) return FLOW_ST_NOT_COMPLETE; - first = 0; + /* In first operator of sequence, AND bit ... MUST be ignored during decoding */ + if (first && isset_and(pos)) + return FLOW_ST_FIRST_AND_BIT_SET; + + /* Zero field MUST be set to 0 on encoding and MUST be ignored during decoding */ + if (pos[0] & 0x08) + return FLOW_ST_ZERO_BIT_SET; + + /* Bitmask operator has additional zero field */ + if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT) + if (pos[0] & 0x04) + return FLOW_ST_ZERO_BIT_SET; + + /* Zero fields in highest nibble of TCP Flags bitmask operand */ + if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2)) + if (pos[1] & 0xf0) + return FLOW_ST_INVALID_TCP_FLAGS; + + /* Zero fields in fragment bitmask operand */ + if ((type == FLOW_TYPE_FRAGMENT) && (len == 1)) + if (pos[1] & (ipv6 ? 0xf1 : 0xf0)) + return FLOW_ST_INVALID_FRAGMENT; + + /* Move to next operator */ + first = false; + last = isset_end(pos); + pos += 1+len; } break; } + default: return FLOW_ST_UNKNOWN_COMPONENT; } @@ -571,32 +738,32 @@ /** * flow4_validate - check untrustworthy IPv4 flowspec data stream - * @nlri: flowspec data stream without compressed encoded length value + * @nlri: flowspec data stream without length header * @len: length of @nlri * - * This function checks meaningfulness of binary flowspec. It should return - * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it - * returns some other %FLOW_ST_xxx state. + * This function checks syntactic correctness of binary flowspec. It returns + * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed + * or non-normalized. */ inline enum flow_validated_state flow4_validate(const byte *nlri, uint len) { - return flow_validate(nlri, len, 0); + return flow_validate(nlri, len, false); } /** * flow6_validate - check untrustworthy IPv6 flowspec data stream - * @nlri: flowspec binary stream without encoded length value + * @nlri: flowspec binary stream without length header * @len: length of @nlri * - * This function checks meaningfulness of binary flowspec. It should return - * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it - * returns some other %FLOW_ST_xxx state. + * This function checks syntactic correctness of binary flowspec. It returns + * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed + * or non-normalized. */ inline enum flow_validated_state flow6_validate(const byte *nlri, uint len) { - return flow_validate(nlri, len, 1); + return flow_validate(nlri, len, true); } /** @@ -790,7 +957,13 @@ /* Set the end-bit for operand-value pair of the component */ op |= 0x80; - if (value & 0xff00) + /* Label component values SHOULD be encoded as 4-octet quantities */ + if ((value > 0xffff) || (fb->this_type == FLOW_TYPE_LABEL)) + { + BUFFER_PUSH(fb->data) = op | 0x20; + put_u32(BUFFER_INC(fb->data, 4), value); + } + else if (value > 0xff) { BUFFER_PUSH(fb->data) = op | 0x10; put_u16(BUFFER_INC(fb->data, 2), value); diff -Nru bird3-3.1.7/lib/flowspec.h bird3-3.1.8/lib/flowspec.h --- bird3-3.1.7/lib/flowspec.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/flowspec.h 2026-07-30 15:32:45.000000000 +0000 @@ -137,18 +137,21 @@ FLOW_ST_EXCEED_MAX_PREFIX_OFFSET, FLOW_ST_EXCEED_MAX_VALUE_LENGTH, FLOW_ST_BAD_TYPE_ORDER, - FLOW_ST_AND_BIT_SHOULD_BE_UNSET, - FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED, - FLOW_ST_DEST_PREFIX_REQUIRED, + FLOW_ST_NONZERO_PADDING, + FLOW_ST_FIRST_AND_BIT_SET, + FLOW_ST_ZERO_BIT_SET, FLOW_ST_INVALID_TCP_FLAGS, - FLOW_ST_CANNOT_USE_DONT_FRAGMENT + FLOW_ST_INVALID_FRAGMENT, }; const char *flow_validated_state_str(enum flow_validated_state code); +enum flow_validated_state flow4_decode(byte *nlri, uint len); +enum flow_validated_state flow6_decode(byte *nlri, uint len); enum flow_validated_state flow4_validate(const byte *nlri, uint len); enum flow_validated_state flow6_validate(const byte *nlri, uint len); -void flow_check_cf_value_length(struct flow_builder *fb, u32 expr); -void flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask); + +void flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u32 mask); +void flow_check_cf_numeric_arg(struct flow_builder *fb, uint val); void flow4_validate_cf(net_addr_flow4 *f); void flow6_validate_cf(net_addr_flow6 *f); diff -Nru bird3-3.1.7/lib/flowspec_test.c bird3-3.1.8/lib/flowspec_test.c --- bird3-3.1.7/lib/flowspec_test.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/flowspec_test.c 2026-07-30 15:32:45.000000000 +0000 @@ -282,6 +282,13 @@ }) ), TS( + FLOW_ST_EXCEED_MAX_VALUE_LENGTH, + "DSCP of length 2", + ((byte []) { + FLOW_TYPE_DSCP, 0x91, 00, 63, + }) + ), + TS( FLOW_ST_BAD_TYPE_ORDER, "Bad flowspec component type order", ((byte []) { @@ -298,28 +305,65 @@ }) ), TS( - FLOW_ST_AND_BIT_SHOULD_BE_UNSET, + FLOW_ST_UNKNOWN_COMPONENT, + "Unknown component of type number 14", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, + 14 /*something new*/, 0x80, 0x55, + }) + ), + TS( + FLOW_ST_UNKNOWN_COMPONENT, + "Label component in IPv4", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55, + }) + ), + TS( + FLOW_ST_NONZERO_PADDING, + "Non-zero padding in prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 20, 5, 6, 7, + }) + ), + TS( + FLOW_ST_FIRST_AND_BIT_SET, "The first numeric operator has set the AND bit", ((byte []) { FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, }) ), TS( - FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED, - "Set zero bit in operand to one", + FLOW_ST_ZERO_BIT_SET, + "Set zero bit in operator to one (0x08)", ((byte []) { FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06, }) ), TS( - FLOW_ST_UNKNOWN_COMPONENT, - "Unknown component of type number 13", + FLOW_ST_ZERO_BIT_SET, + "Set zero bit in operator to one (0x04)", ((byte []) { - FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, - FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, - 13 /*something new*/, 0x80, 0x55, + FLOW_TYPE_FRAGMENT, 0x84, 0x03, }) ), + TS( + FLOW_ST_INVALID_TCP_FLAGS, + "Set zero bit in operand to one (TCP flags)", + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a, + }) + ), + TS( + FLOW_ST_INVALID_FRAGMENT, + "Set zero bit in operand to one (fragment)", + ((byte []) { + FLOW_TYPE_FRAGMENT, 0x81, 0xff, + }) + ), + }; #undef TS @@ -338,7 +382,7 @@ enum flow_validated_state res; byte nlri1[] = { - FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98, + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0, FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, @@ -393,6 +437,13 @@ }) ), TS( + FLOW_ST_EXCEED_MAX_VALUE_LENGTH, + "Fragment of length 2", + ((byte []) { + FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f, + }) + ), + TS( FLOW_ST_BAD_TYPE_ORDER, "Bad flowspec component type order", ((byte []) { @@ -404,46 +455,418 @@ FLOW_ST_BAD_TYPE_ORDER, "Doubled destination prefix component", ((byte []) { - FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98, - FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98, + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, }) ), TS( - FLOW_ST_AND_BIT_SHOULD_BE_UNSET, + FLOW_ST_UNKNOWN_COMPONENT, + "Unknown component of type number 14", + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, + 14 /*something new*/, 0x80, 0x55, + }) + ), + TS( + FLOW_ST_VALID, + "Label component is well-known in IPv6", + ((byte []) { + FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55, + }) + ), + TS( + FLOW_ST_NONZERO_PADDING, + "Non-zero padding in prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 44, 16, 0x40, 0x12, 0x34, 0x56, + }) + ), + TS( + FLOW_ST_FIRST_AND_BIT_SET, "The first numeric operator has set the AND bit", ((byte []) { FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90 }) ), TS( - FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED, - "Set zero bit in operand to one", + FLOW_ST_ZERO_BIT_SET, + "Set zero bit in operator to one (0x08)", ((byte []) { FLOW_TYPE_NEXT_HEADER, 0x89, 0x06 }) ), TS( + FLOW_ST_ZERO_BIT_SET, + "Set zero bit in operator to one (0x04)", + ((byte []) { + FLOW_TYPE_FRAGMENT, 0x84, 0x03, + }) + ), + TS( + FLOW_ST_INVALID_TCP_FLAGS, + "Set zero bit in operand to one (TCP flags)", + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a, + }) + ), + TS( + FLOW_ST_INVALID_FRAGMENT, + "Set zero bit in operand to one (fragment)", + ((byte []) { + FLOW_TYPE_FRAGMENT, 0x81, 0xff, + }) + ), + }; +#undef TS + + for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++) + { + res = flow6_validate(tset[tcase].nlri, tset[tcase].size); + bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description); + } + + return 1; +} + +static int +t_decoding4(void) +{ + enum flow_validated_state res; + + byte nlri1[] = { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13, + FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06, + FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, + FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, + }; + + /* Empty NLRI */ + res = flow4_decode(nlri1, 0); + bt_assert(res == FLOW_ST_VALID); + + /* Valid / Not Complete testing */ + uint valid_sizes[] = {5, 11, 14, 22, 25, 0}; + uint valid_idx = 0; + for (uint size = 1; size <= sizeof(nlri1); size++) + { + res = flow4_decode(nlri1, size); + bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res)); + if (size == valid_sizes[valid_idx]) + { + valid_idx++; + bt_assert(res == FLOW_ST_VALID); + } + else + { + bt_assert(res == FLOW_ST_NOT_COMPLETE); + } + } + + /* Misc err tests */ + + struct tset { + enum flow_validated_state expect; + char *description; + u16 size; + byte *nlri; + byte *result; + }; + +#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data), (data), (result)}) + struct tset tset[] = { + TS( + FLOW_ST_EXCEED_MAX_PREFIX_LENGTH, + "33-length IPv4 prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9 + }), + NULL + ), + TS( + FLOW_ST_EXCEED_MAX_VALUE_LENGTH, + "DSCP of length 2", + ((byte []) { + FLOW_TYPE_DSCP, 0x91, 00, 63, + }), + NULL + ), + TS( + FLOW_ST_BAD_TYPE_ORDER, + "Bad flowspec component type order", + ((byte []) { + FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13, + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + }), + NULL + ), + TS( + FLOW_ST_BAD_TYPE_ORDER, + "Doubled destination prefix component", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + }), + NULL + ), + TS( + FLOW_ST_UNKNOWN_COMPONENT, + "Unknown component of type number 14", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, + 14 /*something new*/, 0x80, 0x55, + }), + NULL + ), + TS( + FLOW_ST_UNKNOWN_COMPONENT, + "Label component in IPv4", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, + FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55, + }), + NULL + ), + TS( + FLOW_ST_VALID, + "Non-zero padding in prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x7f, + FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f, + }), + ((byte []) { + FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x78, + FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f, + }) + ), + TS( FLOW_ST_VALID, - "Component of type number 13 (Label) is well-known in IPv6", + "The first numeric operator has set the AND bit", + ((byte []) { + FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, + }), + ((byte []) { + FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, + }) + ), + TS( + FLOW_ST_VALID, + "Set zero bit in operator to one", + ((byte []) { + FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06, + FLOW_TYPE_FRAGMENT, 0x84, 0x03, + }), ((byte []) { - FLOW_TYPE_LABEL, 0x80, 0x55 + FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06, + FLOW_TYPE_FRAGMENT, 0x80, 0x03, }) ), TS( + FLOW_ST_VALID, + "Set zero bit in operand to one", + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a, + FLOW_TYPE_FRAGMENT, 0x81, 0xff, + }), + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a, + FLOW_TYPE_FRAGMENT, 0x81, 0x0f, + }) + ), + }; +#undef TS + + for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++) + { + res = flow4_decode(tset[tcase].nlri, tset[tcase].size); + bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description); + + if ((res == FLOW_ST_VALID) && (tset[tcase].expect == FLOW_ST_VALID)) + { + int pos; + for (pos = 0; pos < tset[tcase].size; pos++) + if (tset[tcase].nlri[pos] != tset[tcase].result[pos]) + break; + + bool ok = (pos == tset[tcase].size); + bt_assert_msg(ok, "Assertion (pos %u: 0x%02x == 0x%02x) %s", pos, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tset[tcase].description); + } + } + + return 1; +} + +static int +t_decoding6(void) +{ + enum flow_validated_state res; + + byte nlri1[] = { + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, + FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0, + FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, + FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, + FLOW_TYPE_LABEL, 0x80, 0x55, + }; + + /* Isn't included destination prefix */ + res = flow6_decode(nlri1, 0); + bt_assert(res == FLOW_ST_VALID); + + /* Valid / Not Complete testing */ + uint valid_sizes[] = {0, 9, 13, 16, 24, 27, 0}; + uint valid_idx = 0; + for (uint size = 0; size <= sizeof(nlri1); size++) + { + res = flow6_decode(nlri1, size); + bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res)); + if (size == valid_sizes[valid_idx]) + { + valid_idx++; + bt_assert(res == FLOW_ST_VALID); + } + else + { + bt_assert(res == FLOW_ST_NOT_COMPLETE); + } + } + + /* Misc err tests */ + + struct tset { + enum flow_validated_state expect; + char *description; + u16 size; + byte *nlri; + byte *result; + }; + +#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data), (data), (result)}) + struct tset tset[] = { + TS( + FLOW_ST_EXCEED_MAX_PREFIX_LENGTH, + "129-length IPv6 prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 129, 64, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12 + }), + NULL + ), + TS( + FLOW_ST_EXCEED_MAX_PREFIX_OFFSET, + "Prefix offset is higher than prefix length", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34 + }), + NULL + ), + TS( + FLOW_ST_EXCEED_MAX_VALUE_LENGTH, + "Fragment of length 2", + ((byte []) { + FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f, + }), + NULL + ), + TS( + FLOW_ST_BAD_TYPE_ORDER, + "Bad flowspec component type order", + ((byte []) { + FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, + FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0, + }), + NULL + ), + TS( + FLOW_ST_BAD_TYPE_ORDER, + "Doubled destination prefix component", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, + FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0, + }), + NULL + ), + TS( FLOW_ST_UNKNOWN_COMPONENT, "Unknown component of type number 14", ((byte []) { - FLOW_TYPE_LABEL, 0x80, 0x55, + FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, 14 /*something new*/, 0x80, 0x55, + }), + NULL + ), + TS( + FLOW_ST_VALID, + "Label component is well-known in IPv6", + ((byte []) { + FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55, + }), + ((byte []) { + FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55, + }) + ), + TS( + FLOW_ST_VALID, + "Non-zero padding in prefix", + ((byte []) { + FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x7c, + }), + ((byte []) { + FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x60, }) - ) + ), + TS( + FLOW_ST_VALID, + "The first numeric operator has set the AND bit", + ((byte []) { + FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90 + }), + ((byte []) { + FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, + }) + ), + TS( + FLOW_ST_VALID, + "Set zero bit in operator to one", + ((byte []) { + FLOW_TYPE_NEXT_HEADER, 0x89, 0x06, + FLOW_TYPE_FRAGMENT, 0x84, 0x02, + }), + ((byte []) { + FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06, + FLOW_TYPE_FRAGMENT, 0x80, 0x02, + }) + ), + TS( + FLOW_ST_VALID, + "Set zero bit in operand to one", + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a, + FLOW_TYPE_FRAGMENT, 0x81, 0xff, + }), + ((byte []) { + FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a, + FLOW_TYPE_FRAGMENT, 0x81, 0x0e, + }) + ), }; #undef TS for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++) { - res = flow6_validate(tset[tcase].nlri, tset[tcase].size); + res = flow6_decode(tset[tcase].nlri, tset[tcase].size); bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description); + + if ((res == FLOW_ST_VALID) && (tset[tcase].expect == FLOW_ST_VALID)) + { + int pos; + for (pos = 0; pos < tset[tcase].size; pos++) + if (tset[tcase].nlri[pos] != tset[tcase].result[pos]) + break; + + bool ok = (pos == tset[tcase].size); + bt_assert_msg(ok, "Assertion (pos %u: 0x%02x == 0x%02x) %s", pos, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tset[tcase].description); + } } return 1; @@ -546,7 +969,7 @@ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0, FLOW_TYPE_NEXT_HEADER, 0x80, 0x06, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, - FLOW_TYPE_LABEL, 0x80, 0x55, + FLOW_TYPE_LABEL, 0xa0, 0x00, 0x00, 0x00, 0x55, ); /* Normal order */ @@ -729,6 +1152,8 @@ bt_test_suite(t_accessors6, "Testing accessors (IPv6)"); bt_test_suite(t_validation4, "Testing validation (IPv4)"); bt_test_suite(t_validation6, "Testing validation (IPv6)"); + bt_test_suite(t_decoding4, "Testing decoding (IPv4)"); + bt_test_suite(t_decoding6, "Testing decoding (IPv6)"); bt_test_suite(t_builder4, "Inserting components into existing Flow Specification (IPv4)"); bt_test_suite(t_builder6, "Inserting components into existing Flow Specification (IPv6)"); bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation"); diff -Nru bird3-3.1.7/lib/lockfree.c bird3-3.1.8/lib/lockfree.c --- bird3-3.1.7/lib/lockfree.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/lockfree.c 2026-07-30 15:32:45.000000000 +0000 @@ -10,7 +10,7 @@ #include "lib/birdlib.h" #include "lib/lockfree.h" -#define LOCAL_DEBUG +#undef LOCAL_DEBUG void lfuc_unlock_deferred(struct deferred_call *dc) { @@ -87,6 +87,10 @@ if (!block) { block = alloc_page(); +#ifdef DEBUGGING + memset(block, POISON_LFJOUR_NEXT, page_size); +#endif + lfjour_debug("lfjour(%p)_push_prepare: allocating block %p", j, block); *block = (struct lfjour_block) {}; lfjour_block_add_tail(&j->pending, block); @@ -534,11 +538,21 @@ u64 orig_first_seq = first->seq; + struct lfjour_item *first_to_poison = NULL, *last_to_poison = NULL; + /* Now we do the actual cleanup */ while (first && (first->seq <= min_seq)) { j->item_done(j, first); + /* Beware. We can't poison lfjour items here because there may still be + * readers of the "done" items. We'll poison them later after RCU synchronizes. */ + last_to_poison = first; + + if (!first_to_poison) + first_to_poison = first; + + /* Find next journal item */ struct lfjour_item *next = lfjour_get_next(j, first); if (PAGE_HEAD(next) != PAGE_HEAD(first)) @@ -555,11 +569,14 @@ synchronize_rcu(); /* Now we can finally drop the block */ -#ifdef LOCAL_DEBUG - memset(block, 0xbe, page_size); +#ifdef DEBUGGING + memset(block, POISON_LFJOUR_PAGE, page_size); #endif free_page(block); + /* Do not poison items from a freed page */ + first_to_poison = last_to_poison = NULL; + /* If no more blocks are remaining, we shall reset * the sequence numbers */ @@ -577,6 +594,18 @@ first = next; } + /* Wait for possible pending readers of the partially done block */ + synchronize_rcu(); + + /* Poison items from a page which is not yet done completely. */ +#ifdef DEBUGGING + if (first_to_poison) + { + ASSERT_DIE(PAGE_HEAD(first_to_poison) == PAGE_HEAD(last_to_poison)); + memset(first_to_poison, POISON_LFJOUR_ITEM, (last_to_poison - first_to_poison) + j->item_size); + } +#endif + lfjour_cleanup_done(j); CALL(j->cleanup_done, j, orig_first_seq, first ? first->seq : ~((u64) 0)); diff -Nru bird3-3.1.7/lib/mempool.c bird3-3.1.8/lib/mempool.c --- bird3-3.1.7/lib/mempool.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/mempool.c 2026-07-30 15:32:45.000000000 +0000 @@ -124,6 +124,9 @@ /* Need to allocate a new chunk */ c = alloc_page(); +#ifdef DEBUGGING + memset(c, POISON_LP_NEW_PAGE, page_size); +#endif m->total += LP_DATA_SIZE; c->next = NULL; @@ -254,6 +257,9 @@ while (c = m->current->next) { m->current->next = c->next; +#ifdef DEBUGGING + memset(c, POISON_LP_FREE_PAGE, page_size); +#endif free_page(c); } } diff -Nru bird3-3.1.7/lib/obstacle.h bird3-3.1.8/lib/obstacle.h --- bird3-3.1.7/lib/obstacle.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/obstacle.h 2026-07-30 15:32:45.000000000 +0000 @@ -73,6 +73,16 @@ return len; } +static inline void +obstacle_target_cleanup(struct obstacle_target *t) +{ + /* Call this from the done callback */ + ASSERT_DIE(!obstacle_target_count(t)); + + mb_free((void *) DOMAIN_NAME(resource, t->dom)); + DOMAIN_FREE(resource, t->dom); +} + #define OBSREF(_type) struct { _type *ref; struct obstacle o; } #define OBSREF_SET(_ref, _val) ({ \ diff -Nru bird3-3.1.7/lib/rcu.h bird3-3.1.8/lib/rcu.h --- bird3-3.1.7/lib/rcu.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/rcu.h 2026-07-30 15:32:45.000000000 +0000 @@ -29,8 +29,20 @@ extern _Thread_local struct rcu_thread this_rcu_thread; +static inline bool rcu_read_active(void) +{ + return !!(this_rcu_thread.local_ctl & RCU_NEST_MASK); +} + +void page_fill_hot(void); + static inline void rcu_read_lock(void) { + /* Be prepared for possible page allocations during critical sections. + * That is not nice but it happens anyway. */ + if (!rcu_read_active()) + page_fill_hot(); + /* Increment the nesting counter */ atomic_store_explicit(&this_rcu_thread.ctl, (this_rcu_thread.local_ctl += RCU_NEST_CNT), memory_order_release); @@ -54,11 +66,6 @@ this_rcu_thread.local_ctl--; } -static inline bool rcu_read_active(void) -{ - return !!(this_rcu_thread.local_ctl & RCU_NEST_MASK); -} - void synchronize_rcu(void); /* Registering and unregistering a birdloop. To be called from birdloop implementation */ diff -Nru bird3-3.1.7/lib/resource.c bird3-3.1.8/lib/resource.c --- bird3-3.1.7/lib/resource.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/resource.c 2026-07-30 15:32:45.000000000 +0000 @@ -234,8 +234,11 @@ ASSERT_DIE(DG_IS_LOCKED(orig->domain)); resource_rem_node(&orig->inside, r); + uint rsz = r->class->size; + r->class->free(r); r->class = NULL; + memset(r, POISON_RESOURCE_FREE, rsz); xfree(r); } @@ -522,6 +525,7 @@ return; SKIP_BACK_DECLARE(struct mblock, b, data, m); + memset(m, POISON_MB_FREE, b->size); rfree(&b->r); } diff -Nru bird3-3.1.7/lib/resource.h bird3-3.1.8/lib/resource.h --- bird3-3.1.7/lib/resource.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/resource.h 2026-07-30 15:32:45.000000000 +0000 @@ -203,5 +203,29 @@ void xfree(void *); #endif +/* Auxiliary memory poisoning enum for debug purposes. + * Keep spacing for cases where the memory gets modified by 1 before crashing. + * Keep the values sorted by number and always explicit. */ + +enum poison_byte { + POISON_LFJOUR_PAGE = 0x21, + POISON_LFJOUR_ITEM = 0x23, + POISON_LFJOUR_NEXT = 0x25, + + POISON_THREAD_SHUTDOWN = 0x47, + + POISON_RESOURCE_FREE = 0xaa, + + POISON_LP_NEW_PAGE = 0xb3, + POISON_LP_FREE_PAGE = 0xb5, + + POISON_MB_FREE = 0xbc, + + POISON_SLAB_NEW_ITEM = 0xc1, + POISON_SLAB_NEW_PAGE = 0xc3, + POISON_SLAB_FREE_PAGE = 0xc5, + POISON_SLAB_FREE_ITEM = 0xc7, +}; + #endif diff -Nru bird3-3.1.7/lib/slab.c bird3-3.1.8/lib/slab.c --- bird3-3.1.7/lib/slab.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/slab.c 2026-07-30 15:32:45.000000000 +0000 @@ -278,7 +278,7 @@ void *out = ((void *) h) + s->head_size + (i * 32 + pos) * s->obj_size; #ifdef POISON - memset(out, 0xcd, s->data_size); + memset(out, POISON_SLAB_NEW_ITEM, s->data_size); #endif return out; } @@ -298,7 +298,7 @@ ASSERT_DIE(SL_GET_HEAD(h) == h); #ifdef POISON - memset(h, 0xba, page_size); + memset(h, POISON_SLAB_NEW_PAGE, page_size); #endif memset(h, 0, s->head_size); @@ -339,7 +339,7 @@ ASSERT_DIE(DG_IS_LOCKED(resource_parent(&s->r)->domain)); #ifdef POISON - memset(oo, 0xdb, s->data_size); + memset(oo, POISON_SLAB_FREE_ITEM, s->data_size); #endif uint offset = oo - ((void *) h) - s->head_size; @@ -357,7 +357,7 @@ if (s->num_empty_heads >= MAX_EMPTY_HEADS) { #ifdef POISON - memset(h, 0xde, page_size); + memset(h, POISON_SLAB_FREE_PAGE, page_size); #endif free_page(h); } diff -Nru bird3-3.1.7/lib/string.h bird3-3.1.8/lib/string.h --- bird3-3.1.7/lib/string.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/lib/string.h 2026-07-30 15:32:45.000000000 +0000 @@ -108,6 +108,14 @@ return dest; } +static inline void * +memcpy0(void *dst, const void *src, size_t dst_len, size_t src_len) +{ + memset(dst, 0, dst_len); + memcpy(dst, src, MIN_(dst_len, src_len)); + return dst; +} + #define ROUTER_ID_64_LENGTH 23 #endif diff -Nru bird3-3.1.7/nest/iface.c bird3-3.1.8/nest/iface.c --- bird3-3.1.7/nest/iface.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/nest/iface.c 2026-07-30 15:32:45.000000000 +0000 @@ -211,9 +211,6 @@ return; if_unlink(a->iface); -#if DEBUGGING - memset(a, 0x5b, sizeof(struct ifa)); -#endif mb_free(a); } diff -Nru bird3-3.1.7/nest/password.h bird3-3.1.8/nest/password.h --- bird3-3.1.7/nest/password.h 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/nest/password.h 2026-07-30 15:32:45.000000000 +0000 @@ -28,10 +28,10 @@ struct password_item *password_find_by_value(list *l, char *pass, uint size); void password_validate_length(const struct password_item *p); -static inline int password_verify(struct password_item *p1, char *p2, uint size) +static inline int password_verify(const struct password_item *p1, const char *p2, uint size) { char buf[size]; - strncpy(buf, p1->password, size); + memcpy0(buf, p1->password, size, p1->length); return !memcmp(buf, p2, size); } diff -Nru bird3-3.1.7/nest/proto.c bird3-3.1.8/nest/proto.c --- bird3-3.1.7/nest/proto.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/nest/proto.c 2026-07-30 15:32:45.000000000 +0000 @@ -2133,6 +2133,8 @@ ASSERT_DIE(birdloop_inside(&main_birdloop)); ASSERT_DIE(_graceful_recovery_context.grc_state == GRS_ACTIVE); + obstacle_target_cleanup(&_graceful_recovery_context.obstacles); + tm_stop(&_graceful_recovery_context.wait_timer); log(L_INFO "Graceful recovery done"); diff -Nru bird3-3.1.7/nest/rt-table.c bird3-3.1.8/nest/rt-table.c --- bird3-3.1.7/nest/rt-table.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/nest/rt-table.c 2026-07-30 15:32:45.000000000 +0000 @@ -2722,8 +2722,13 @@ if (rcnt || ocnt || ecnt) { - if (!ecnt && prefilter && !prefilter(f, NET_READ_BEST_ROUTE(tr, n)->rte.net)) - return NULL; + if (!ecnt && prefilter) + { + struct rte_storage *best = NET_READ_BEST_ROUTE(tr, n); + + if (best && !prefilter(f, best->rte.net)) + return NULL; + } feed = rt_alloc_feed(rcnt+ocnt, ecnt); diff -Nru bird3-3.1.7/proto/bfd/packets.c bird3-3.1.8/proto/bfd/packets.c --- bird3-3.1.7/proto/bfd/packets.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/bfd/packets.c 2026-07-30 15:32:45.000000000 +0000 @@ -148,7 +148,7 @@ { struct bfd_crypto_auth *auth = (void *) (pkt + 1); uint hash_alg = bfd_auth_type_to_hash_alg[cf->auth_type]; - uint hash_len = mac_type_length(pass->alg); + uint hash_len = mac_type_length(hash_alg); /* Increase CSN about one time per second */ u32 new_time = (u64) current_time() >> 20; @@ -169,7 +169,8 @@ pkt->flags |= BFD_FLAG_AP; pkt->length += auth->length; - strncpy(auth->data, pass->password, hash_len); + memcpy0(auth->data, pass->password, hash_len, pass->length); + mac_fill(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth->data); return; } @@ -262,7 +263,7 @@ byte *auth_data = alloca(hash_len); memcpy(auth_data, auth->data, hash_len); - strncpy(auth->data, pass->password, hash_len); + memcpy0(auth->data, pass->password, hash_len, pass->length); if (!mac_verify(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth_data)) DROP("wrong authentication code", pass->id); diff -Nru bird3-3.1.7/proto/bgp/bgp.c bird3-3.1.8/proto/bgp/bgp.c --- bird3-3.1.7/proto/bgp/bgp.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/bgp/bgp.c 2026-07-30 15:32:45.000000000 +0000 @@ -1338,15 +1338,15 @@ if (p->p.proto_state == PS_UP) bgp_stop(p, 0, NULL, 0); - uint adsz; - struct bgp_session_close_ad *bscad = alloca(adsz = sizeof *bscad + conn->notify_size); + uint ad_size = sizeof(struct bgp_session_close_ad) + conn->notify_size; + struct bgp_session_close_ad *bscad = alloca(ad_size); *bscad = (struct bgp_session_close_ad) { - .ad.length = adsz - sizeof(adata), + .ad.length = ad_size - sizeof(adata), .last_error_class = p->last_error_class, .notify_code = conn->notify_code, .notify_subcode = conn->notify_subcode, }; - memcpy(bscad->data, conn->notify_data, conn->notify_size); + bmemcpy(bscad->data, conn->notify_data, conn->notify_size); ea_list *pes = p->p.ea_state; ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->ad)); @@ -3009,6 +3009,9 @@ if (cf->keepalive_time > (cf->hold_time / 2)) log(L_WARN "Keepalive time should be at most 1/2 of hold time"); + if ((cf->send_hold_time > 0) && (cf->send_hold_time < cf->hold_time)) + cf_error("Send hold time must be zero or at least hold time"); + if (cf->min_hold_time > cf->hold_time) cf_error("Min hold time (%u) exceeds hold time (%u)", cf->min_hold_time, cf->hold_time); diff -Nru bird3-3.1.7/proto/bgp/packets.c bird3-3.1.8/proto/bgp/packets.c --- bird3-3.1.7/proto/bgp/packets.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/bgp/packets.c 2026-07-30 15:32:45.000000000 +0000 @@ -936,12 +936,14 @@ /* Compute effective hold and keepalive times */ uint hold_time = MIN(hold, p->cf->hold_time); + uint base_hold_time = p->cf->hold_time ?: 1; + uint keepalive_time = p->cf->keepalive_time ? - (p->cf->keepalive_time * hold_time / p->cf->hold_time) : + (p->cf->keepalive_time * hold_time / base_hold_time) : hold_time / 3; uint send_hold_time = (p->cf->send_hold_time >= 0) ? - (p->cf->send_hold_time * hold_time / p->cf->hold_time) : + ((u64) p->cf->send_hold_time * hold_time / base_hold_time) : 2 * hold_time; /* Keepalive time might be rounded down to zero */ @@ -2101,8 +2103,8 @@ if (len < flen) bgp_parse_error(s, 1); - /* Validate flow data */ - enum flow_validated_state r = flow4_validate(data, dlen); + /* Decode flow data */ + enum flow_validated_state r = flow4_decode(data, dlen); if (r != FLOW_ST_VALID) { log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r)); @@ -2113,7 +2115,7 @@ uint pxlen = 0; /* Decode dst prefix */ - if (data[0] == FLOW_TYPE_DST_PREFIX) + if (dlen && (data[0] == FLOW_TYPE_DST_PREFIX)) { px = flow_read_ip4_part(data); pxlen = flow_read_pxlen(data); @@ -2189,8 +2191,8 @@ if (len < flen) bgp_parse_error(s, 1); - /* Validate flow data */ - enum flow_validated_state r = flow6_validate(data, dlen); + /* Decode flow data */ + enum flow_validated_state r = flow6_decode(data, dlen); if (r != FLOW_ST_VALID) { log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r)); @@ -2201,7 +2203,7 @@ uint pxlen = 0; /* Decode dst prefix */ - if (data[0] == FLOW_TYPE_DST_PREFIX) + if (dlen && (data[0] == FLOW_TYPE_DST_PREFIX)) { px = flow_read_ip6_part(data); pxlen = flow_read_pxlen(data); diff -Nru bird3-3.1.7/proto/l3vpn/l3vpn.c bird3-3.1.8/proto/l3vpn/l3vpn.c --- bird3-3.1.7/proto/l3vpn/l3vpn.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/l3vpn/l3vpn.c 2026-07-30 15:32:45.000000000 +0000 @@ -214,6 +214,7 @@ /* Hostentry also validn't */ ea_unset_attr(&new->attrs, 0, &ea_gen_hostentry); + ea_unset_attr(&new->attrs, 0, &ea_gen_hostentry_version); if (export) { diff -Nru bird3-3.1.7/proto/ospf/packet.c bird3-3.1.8/proto/ospf/packet.c --- bird3-3.1.7/proto/ospf/packet.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/ospf/packet.c 2026-07-30 15:32:45.000000000 +0000 @@ -64,7 +64,8 @@ log(L_ERR "No suitable password found for authentication"); return; } - strncpy(auth->password, pass->password, sizeof(auth->password)); + memcpy0(auth->password, pass->password, sizeof(auth->password), pass->length); + /* fallthrough */ case OSPF_AUTH_NONE: @@ -111,7 +112,7 @@ /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ if (pass->alg < ALG_HMAC) - strncpy(auth_tail, pass->password, auth_len); + memcpy0(auth_tail, pass->password, auth_len, pass->length); else memset32(auth_tail, HMAC_MAGIC, auth_len / 4); @@ -235,7 +236,7 @@ /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ if (pass->alg < ALG_HMAC) - strncpy(auth_tail, pass->password, auth_len); + memcpy0(auth_tail, pass->password, auth_len, pass->length); else memset32(auth_tail, HMAC_MAGIC, auth_len / 4); diff -Nru bird3-3.1.7/proto/pipe/pipe.c bird3-3.1.8/proto/pipe/pipe.c --- bird3-3.1.7/proto/pipe/pipe.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/pipe/pipe.c 2026-07-30 15:32:45.000000000 +0000 @@ -62,6 +62,7 @@ e0.generation = new->generation + 1; ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry); + ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry_version); rte_update(dst, n, &e0, new->src); } diff -Nru bird3-3.1.7/proto/rip/packets.c bird3-3.1.8/proto/rip/packets.c --- bird3-3.1.7/proto/rip/packets.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/proto/rip/packets.c 2026-07-30 15:32:45.000000000 +0000 @@ -274,7 +274,7 @@ case RIP_AUTH_PLAIN: auth->must_be_ffff = htons(0xffff); auth->auth_type = htons(RIP_AUTH_PLAIN); - strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH); + memcpy0(auth->password, pass->password, sizeof(auth->password), pass->length); return; case RIP_AUTH_CRYPTO: @@ -313,7 +313,7 @@ /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ if (pass->alg < ALG_HMAC) - strncpy(tail->auth_data, pass->password, auth_len); + memcpy0(tail->auth_data, pass->password, auth_len, pass->length); else memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); @@ -402,7 +402,7 @@ /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ if (pass->alg < ALG_HMAC) - strncpy(tail->auth_data, pass->password, auth_len); + memcpy0(tail->auth_data, pass->password, auth_len, pass->length); else memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); diff -Nru bird3-3.1.7/sysdep/unix/alloc.c bird3-3.1.8/sysdep/unix/alloc.c --- bird3-3.1.7/sysdep/unix/alloc.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/sysdep/unix/alloc.c 2026-07-30 15:32:45.000000000 +0000 @@ -203,6 +203,8 @@ return NULL; } +static void *alloc_page_global(void); + void * alloc_page(void) { @@ -234,7 +236,14 @@ ASSERT_DIE(pages_kept_here == 0); + return alloc_page_global(); +} + +static void * +alloc_page_global(void) +{ /* If there is any free page kept hot in global storage, we use it. */ + struct free_page *fp; if (fp = alloc_hot_page(fp)) return fp; @@ -475,6 +484,14 @@ ajlog(NULL, NULL, 0, AJT_CLEANUP_END); } + +void +page_fill_hot(void) +{ + while (pages_kept_here < KEEP_PAGES_MAX_LOCAL) + free_page(alloc_page_global()); +} + #endif void diff -Nru bird3-3.1.7/sysdep/unix/io-loop.c bird3-3.1.8/sysdep/unix/io-loop.c --- bird3-3.1.7/sysdep/unix/io-loop.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/sysdep/unix/io-loop.c 2026-07-30 15:32:45.000000000 +0000 @@ -1256,7 +1256,7 @@ /* Last try to run the priority event list; ruin it then to be extra sure */ ev_run_list(&this_thread->priority_events); - memset(&this_thread->priority_events, 0xa5, sizeof(this_thread->priority_events)); + memset(&this_thread->priority_events, POISON_THREAD_SHUTDOWN, sizeof(this_thread->priority_events)); /* Drop loops including the thread dropper itself */ while (!EMPTY_TLIST(birdloop, &thr->loops)) diff -Nru bird3-3.1.7/sysdep/unix/log.c bird3-3.1.8/sysdep/unix/log.c --- bird3-3.1.7/sysdep/unix/log.c 2026-06-09 09:02:34.000000000 +0000 +++ bird3-3.1.8/sysdep/unix/log.c 2026-07-30 15:32:45.000000000 +0000 @@ -164,26 +164,21 @@ ev_send_loop(&main_birdloop, <s->lts_event); } -static void +static bool log_rotate(struct log_channel *lc) { struct rfile *old_rf = atomic_load_explicit(&lc->rf, memory_order_relaxed); lts_request(NULL, old_rf, "Log Rotate Close Old File"); if ((rename(lc->filename, lc->backup) < 0) && (unlink(lc->filename) < 0)) - { - atomic_store_explicit(&lc->rf, NULL, memory_order_relaxed); - return lts_request(lc, NULL, "Log Rotate Failed"); - } + return lts_request(lc, NULL, "Log Rotate Failed"), false; struct rfile *rf = rf_open(log_pool, lc->filename, RF_APPEND, lc->limit); if (!rf) - { - atomic_store_explicit(&lc->rf, NULL, memory_order_relaxed); - return lts_request(lc, NULL, "Log Rotate Failed"); - } + return lts_request(lc, NULL, "Log Rotate Failed"), false; atomic_store_explicit(&lc->rf, rf, memory_order_release); + return true; } /** @@ -231,10 +226,12 @@ glogs = &initial_stderr_log; } + /* Log commit may cause the channel to get removed from the list. + * We need to be able to continue walking the list regardless. */ for ( - struct log_channel *l = glogs; l; - l = atomic_load_explicit(&l->next, memory_order_acquire) - ) + struct log_channel * _Atomic *pprev = &global_logs, *l; + l = atomic_load_explicit(pprev, memory_order_acquire); + pprev = &l->next) { uint mask = atomic_load_explicit(&l->mask, memory_order_acquire); if (!(mask & (1 << buf->class))) @@ -288,7 +285,14 @@ break; } - log_rotate(l); + if (!log_rotate(l)) + { + atomic_store_explicit(&l->rf, NULL, memory_order_relaxed); + atomic_store_explicit(pprev, + atomic_load_explicit(&l->next, memory_order_acquire), + memory_order_release); + } + log_unlock(); rf = atomic_load_explicit(&l->rf, memory_order_relaxed);