<html>
    <head>
      <base href="https://bugzilla.netfilter.org/" />
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - Race errors with nft"
   href="https://bugzilla.netfilter.org/show_bug.cgi?id=1402">1402</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>Race errors with nft
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>nftables
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Debian GNU/Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P5
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>nft
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>pablo@netfilter.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>netfilter@morp.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>Summary
-------
To evaluate the atomicity/stability of nftables, I set up the following
experiment:
 - Created two distinct sets of rules, R1 and R2 oaded via `nft -f`
 - In two different screens, started two processes to constantly re-load the
rulesets with no pause between reloads
 - Created a small script that would dump the currently active ruleset and save
it to a file, named after the hashed ruleset contents

The expectation is that the `nft list ruleset` output would always display
either R1 or R2 - no empty ruleset, or a cross between R1 or R2, or any other
output. Those expectations were true the vast majority of the time, but in some
rare cases I get an unexpeced output - hence the report.

Bug Summary
-------
When competing nft ruleset imports are running, and the ruleset is dumped (with
`nft list ruleset`), a couple of rare errors can be seen:
 1. An error message returned by `nft list ruleset`: "free(): double free
detected in tcache 2"
 2. Output is composed of duplicate (or even trippled) dumps of the same
tables/rules 


Test files to reproduce
-----------------------
R1.tf
```
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    set DROP-NETS-V4 {
        type ipv4_addr
        flags interval
        elements = { 1.2.3.0/27 }
    }

    chain predefrag {
        type filter hook prerouting priority -450; policy accept;
        ip frag-off & 49151 != 0 counter packets 0 bytes 0 drop
        ip6 nexthdr ipv6-frag counter packets 0 bytes 0 drop
    }

    chain INPUT {
        type filter hook input priority 0; policy drop;
        udp sport domain udp dport 1024-65535 accept
        tcp sport domain tcp dport 1024-65535 accept
        udp dport domain ip daddr 127.0.0.1 accept
        udp sport 1024-65535 udp dport 1024-65535 ip ttl 1 counter reject
        udp sport 1024-65535 udp dport 1024-65535 ip6 hoplimit 1 counter reject
        ip saddr @DROP-NETS-V4 counter drop
        ct state invalid drop
        ct state established,related accept
        iifname "lo" counter accept
        meta l4proto icmp counter accept
        meta l4proto ipv6-icmp counter accept
        tcp dport 22 accept
    }

}
```

R2.tf
```
#!/usr/sbin/nft -f

flush ruleset
add table inet my-filter

add set inet my-filter ALLOW_SSH_NET {
  type ipv4_addr;
  flags interval;
  elements={
    0.0.0.0/0
  }
}

add chain inet my-filter FORWARD {type filter hook forward priority 0; policy
drop;}

add chain inet my-filter INPUT {type filter hook input priority 0; policy
drop;}
add rule inet my-filter INPUT ip protocol icmp accept
add rule inet my-filter INPUT ip saddr @ALLOW_SSH_NET accept
add rule inet my-filter INPUT ct state related,established accept
```

nftables_hash.sh:
```
#!/usr/bin/env bash

output=$(nft list ruleset  2>&1 | grep -v counter)
md5=$(md5sum <<< $output | cut -f 1 -d ' ')
$(cat <<< "$output" > "hash_${md5}.out")
echo $(date) "$md5"
```

Test commands
-------------
screen -S R1 bash -c 'while true; do ./R1.nft && echo $(date) "Reloaded R1
ruleset"; done'
screen -S R2 bash -c 'while true; do ./R2.nft && echo $(date) "Reloaded R2
ruleset"; done'
screen -S ruleset_hash bash -c 'while true; do ./nftables_hash.sh | tee
hashes_nft.txt; done'

Results
-------
After running for a few minutes, you would see more than 2 modes of 'nftables
list ruleset' outputs:
# ls -la hash_*
-rw-r--r-- 1 root root   41 Jan 30 14:30
hash_45b12278a9565f2b5d7107d1be71c93a.out
-rw-r--r-- 1 root root  966 Jan 30 14:30
hash_911e7f4ec5194058a36774026e22001b.out
-rw-r--r-- 1 root root 1035 Jan 30 14:30
hash_c4d068fa75ff27bccc495a3e3a550018.out
-rw-r--r-- 1 root root  363 Jan 30 14:30
hash_ce58eea5fcc69506faccd30dc0b68868.out
-rw-r--r-- 1 root root  483 Jan 30 14:30
hash_d33d213b4cfcc7bc056cab22b8d23196.out

The most frequently found error mode is where nft ruleset dumping:
```
# cat hash_45b12278a9565f2b5d7107d1be71c93a.out
free(): double free detected in tcache 2
```

Another error mode is where the dump contains duplicated version of the
ruleset, e.g.
```
# cat hash_911e7f4ec5194058a36774026e22001b.out
table inet filter {
    set DROP-NETS-V4 {
        type ipv4_addr
        flags interval
        elements = { 1.2.3.0/27 }
    }

    chain predefrag {
        type filter hook prerouting priority -450; policy accept;
    }

    chain INPUT {
        type filter hook input priority 0; policy drop;
        udp sport domain udp dport 1024-65535 accept
        tcp sport domain tcp dport 1024-65535 accept
        udp dport domain ip daddr 127.0.0.1 accept
        ct state invalid drop
        ct state established,related accept
        tcp dport ssh accept
    }
}
table inet filter {
    set DROP-NETS-V4 {
        type ipv4_addr
        flags interval
        elements = { 1.2.3.0/27 }
    }

    chain predefrag {
        type filter hook prerouting priority -450; policy accept;
    }

    chain INPUT {
        type filter hook input priority 0; policy drop;
        udp sport domain udp dport 1024-65535 accept
        tcp sport domain tcp dport 1024-65535 accept
        udp dport domain ip daddr 127.0.0.1 accept
        ct state invalid drop
        ct state established,related accept
        tcp dport ssh accept
    }
}
```

In (more) rare cases, the same table can even be displayed 3 times, rather than
just doubled.

System details:
---
OS: Debian 10.2
Kernel: 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64
GNU/Linux
nftables version: nftables v0.9.0 (Fearless Fosdick)
Related LKMs:
  # lsmod | grep -i tables
  nf_tables_set          32768  18
  nf_tables             143360  248
nft_ct,nft_reject_inet,nft_counter,nf_tables_set,nft_reject
  nfnetlink              16384  1 nf_tables</pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are watching all bug changes.</li>
      </ul>
    </body>
</html>