Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F117153853
reproduce.py
All Users
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
cr0ntab
Sun, Sep 13, 3:39 AM
2026-09-13 03:39:11 (UTC+0)
Size
2 KB
Referenced Files
None
Subscribers
None
reproduce.py
View Options
#!/usr/bin/env python3
#
# Minimal reproduction for concurrent entry into libvyosconfig.
#
# vyos-http-api runs configure operations in a threadpool
# (_configure_op -> run_in_threadpool(_execute_configure_op)), and that
# workflow builds Config(), which parses the running and session configs via
# ConfigTree(...) -> libvyosconfig from_string. retrieve_op is served on the
# event loop and also builds ConfigTree(res). ctypes releases the GIL for each
# foreign call, so both threads are inside the OCaml library at once, which
# corrupts the parser state and kills the process with SIGSEGV.
#
# This script drives the same library path directly: several threads build
# ConfigTree objects from the default config, compare them, and drop them (so
# ConfigTree.__del__ -> destroy runs too), while other threads parse an invalid
# config and check the error text. The work runs in a child process so a
# segfault is reported instead of killing the script.
#
# Run as root on a VyOS instance:
# python3 reproduce.py
#
# Stock image: "child process killed by SIGSEGV". Exit code 1.
# With the patched python/vyos/configtree.py in place: "0 errors". Exit code 0.
import
signal
import
subprocess
import
sys
import
threading
import
time
CONFIG
=
'/opt/vyatta/etc/config.boot.default'
INVALID
=
'interfaces {
\n
ethernet eth0 {
\n
address
\n
'
THREADS
=
4
SECONDS
=
20
def
child
():
from
vyos.configtree
import
ConfigTree
config
=
open
(
CONFIG
)
.
read
()
reference
=
ConfigTree
(
config
)
deadline
=
time
.
time
()
+
SECONDS
errors
=
[]
counts
=
[
0
]
*
THREADS
def
valid
(
idx
):
while
time
.
time
()
<
deadline
:
tree
=
ConfigTree
(
config
)
if
tree
!=
reference
:
errors
.
append
(
'parsed tree differs from reference'
)
del
tree
counts
[
idx
]
+=
1
def
invalid
(
idx
):
while
time
.
time
()
<
deadline
:
try
:
ConfigTree
(
INVALID
)
errors
.
append
(
'invalid config parsed without error'
)
except
ValueError
as
e
:
if
'Syntax error'
not
in
str
(
e
):
errors
.
append
(
f
'unexpected error text: {str(e)[:80]!r}'
)
counts
[
idx
]
+=
1
threads
=
[
threading
.
Thread
(
target
=
valid
if
i
%
2
==
0
else
invalid
,
args
=
(
i
,))
for
i
in
range
(
THREADS
)
]
for
t
in
threads
:
t
.
start
()
for
t
in
threads
:
t
.
join
()
print
(
f
'{sum(counts)} ConfigTree operations from {THREADS} threads in {SECONDS}s'
)
for
e
in
sorted
(
set
(
errors
))[:
5
]:
print
(
f
' {e}'
)
print
(
f
'{len(errors)} errors'
)
return
1
if
errors
else
0
def
main
():
if
sys
.
argv
[
1
:]
==
[
'--child'
]:
return
child
()
proc
=
subprocess
.
run
([
sys
.
executable
,
__file__
,
'--child'
])
if
proc
.
returncode
<
0
:
name
=
signal
.
Signals
(
-
proc
.
returncode
)
.
name
print
(
f
'child process killed by {name}'
)
return
1
return
proc
.
returncode
if
__name__
==
'__main__'
:
sys
.
exit
(
main
())
File Metadata
Details
Attached
Mime Type
text/x-script.python
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4239220
Default Alt Text
reproduce.py (2 KB)
Attached To
Mode
T9307: http-api: concurrent libvyosconfig entry from configure threadpool and event loop crashes vyos-http-api with SIGSEGV
Attached
Detach File
Event Timeline
Log In to Comment