Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BR]: socket.getaddrinfo segfaults, fail2ban crash on host with ipv6 disabled. #3438

Closed
ptempier opened this issue Jan 5, 2023 · 6 comments

Comments

@ptempier
Copy link

ptempier commented Jan 5, 2023

Some info for future reference, some additional tests

Some not existing host

[root@fail2ban_01 /]# fail2ban-python -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.dnsToIp("faizaza"))'
Unable to find a corresponding IP address for faizaza: [Errno -2] Name or service not known
set()

[root@fail2ban_01 /]# fail2ban-python -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.dnsToIp("google.fr"))'
{'216.58.213.67', '2a00:1450:4007:80d::2003'}

[root@fail2ban_01 /]# fail2ban-python -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.dnsToIp("ip6.nl"))'
{'2a02:2308:10::c:19', '193.200.132.187'}

fail2ban_01 is the container o the host with ipv6 disabled

[root@fail2ban_01 /]# fail2ban-python -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.dnsToIp("fail2ban_01"))'
Segmentation fault (core dumped)

full backtrace :

Program received signal SIGSEGV, Segmentation fault.
0x00007f29c19f24a3 in gaih_inet (name=<optimized out>, name@entry=0x7f29c1683d40 "fail2ban_01", service=service@entry=0x0, req=req@entry=0x7ffd53933320, pai=pai@entry=0x7ffd53932d88, naddrs=naddrs@entry=0x7ffd53932d84, tmpbuf=tmpbuf@entry=0x7ffd53932e80)
    at ../sysdeps/posix/getaddrinfo.c:933
933	      if (at->family == AF_UNSPEC)
(gdb) 
(gdb) 
(gdb) 
(gdb) backtrace
#0  0x00007f29c19f24a3 in gaih_inet (name=<optimized out>, name@entry=0x7f29c1683d40 "fail2ban_01", service=service@entry=0x0, req=req@entry=0x7ffd53933320, pai=pai@entry=0x7ffd53932d88, naddrs=naddrs@entry=0x7ffd53932d84, tmpbuf=tmpbuf@entry=0x7ffd53932e80)
    at ../sysdeps/posix/getaddrinfo.c:933
#1  0x00007f29c19f29c9 in __GI_getaddrinfo (name=<optimized out>, name@entry=0x7f29c1683d40 "fail2ban_01", service=<optimized out>, service@entry=0x0, hints=hints@entry=0x7ffd53933320, pai=pai@entry=0x7ffd53933318) at ../sysdeps/posix/getaddrinfo.c:2240
#2  0x00007f29c148ceab in socket_getaddrinfo (self=<optimized out>, args=<optimized out>, kwargs=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Modules/socketmodule.c:6578
#3  0x00007f29c1be9de2 in cfunction_call (func=<built-in method getaddrinfo of module object at remote 0x7f29c14b64f0>, args=<optimized out>, kwargs=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Objects/methodobject.c:543
#4  0x00007f29c1bdc1c4 in _PyObject_MakeTpCall (tstate=0x56171a7f7a10, callable=<built-in method getaddrinfo of module object at remote 0x7f29c14b64f0>, args=0x56171a82a1c8, nargs=6, keywords=0x0) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Objects/call.c:191
#5  0x00007f29c1bd90e6 in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x56171a82a1c8, callable=<optimized out>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:116
#6  _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x56171a82a1c8, callable=<optimized out>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:103
#7  PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x56171a82a1c8, callable=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:127
#8  call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:5077
#9  _PyEval_EvalFrameDefault (tstate=<optimized out>, f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:3489
#10 0x00007f29c1bd29e5 in _PyEval_EvalFrame (throwflag=0, 
    f=Frame 0x56171a829ff0, for file /usr/lib64/python3.9/socket.py, line 954, in getaddrinfo (host='fail2ban_01', port=None, family=<AddressFamily(_value_=10, _name_='AF_INET6', __objclass__=<EnumMeta(_generate_next_value_=<function at remote 0x7f29c15b4790>, __doc__='An enumeration.', __module__='socket', _member_names_=['AF_UNSPEC', 'AF_UNIX', 'AF_INET', 'AF_AX25', 'AF_IPX', 'AF_APPLETALK', 'AF_NETROM', 'AF_BRIDGE', 'AF_ATMPVC', 'AF_X25', 'AF_INET6', 'AF_ROSE', 'AF_NETBEUI', 'AF_SECURITY', 'AF_KEY', 'AF_NETLINK', 'AF_PACKET', 'AF_ASH', 'AF_ECONET', 'AF_ATMSVC', 'AF_RDS', 'AF_SNA', 'AF_IRDA', 'AF_PPPOX', 'AF_WANPIPE', 'AF_LLC', 'AF_CAN', 'AF_TIPC', 'AF_BLUETOOTH', 'AF_ALG', 'AF_VSOCK', 'AF_QIPCRTR'], _member_map_={'AF_UNSPEC': <AddressFamily(_value_=0, _name_='AF_UNSPEC', __objclass__=<...>) at remote 0x7f29c14d7200>, 'AF_UNIX': <AddressFamily(_value_=1, _name_='AF_UNIX', __objclass__=<...>) at remote 0x7f29c14d7280>, 'AF_INET': <AddressFamily(_value_=2, _name_='AF_INET', __objclass__=<...>) at remote 0x7f29c14d7...(truncated), tstate=0x56171a7f7a10)
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/internal/pycore_ceval.h:40
#11 _PyEval_EvalCode (tstate=<optimized out>, _co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x56171a8fa348, kwcount=0, kwstep=1, defs=0x7f29c14b63c8, defcount=4, kwdefs=0x0, closure=0x0, 
    name='getaddrinfo', qualname='getaddrinfo') at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:4329
#12 0x00007f29c1be0205 in _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Objects/call.c:396
#13 0x00007f29c1bd8ac0 in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x56171a8fa320, callable=<function at remote 0x7f29c14daf70>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:118
#14 PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x56171a8fa320, callable=<function at remote 0x7f29c14daf70>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:127
#15 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:5077
#16 _PyEval_EvalFrameDefault (tstate=<optimized out>, f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:3489
#17 0x00007f29c1be0493 in _PyEval_EvalFrame (throwflag=0, 
    f=Frame 0x56171a8fa160, for file /usr/lib/python3.9/site-packages/fail2ban/server/ipdns.py, line 97, in dnsToIp (dns='fail2ban_01', ips={<IPAddr at remote 0x7f29c12611d0>}, saveerr=None, fam=<AddressFamily(_value_=10, _name_='AF_INET6', __objclass__=<EnumMeta(_generate_next_value_=<function at remote 0x7f29c15b4790>, __doc__='An enumeration.', __module__='socket', _member_names_=['AF_UNSPEC', 'AF_UNIX', 'AF_INET', 'AF_AX25', 'AF_IPX', 'AF_APPLETALK', 'AF_NETROM', 'AF_BRIDGE', 'AF_ATMPVC', 'AF_X25', 'AF_INET6', 'AF_ROSE', 'AF_NETBEUI', 'AF_SECURITY', 'AF_KEY', 'AF_NETLINK', 'AF_PACKET', 'AF_ASH', 'AF_ECONET', 'AF_ATMSVC', 'AF_RDS', 'AF_SNA', 'AF_IRDA', 'AF_PPPOX', 'AF_WANPIPE', 'AF_LLC', 'AF_CAN', 'AF_TIPC', 'AF_BLUETOOTH', 'AF_ALG', 'AF_VSOCK', 'AF_QIPCRTR'], _member_map_={'AF_UNSPEC': <AddressFamily(_value_=0, _name_='AF_UNSPEC', __objclass__=<...>) at remote 0x7f29c14d7200>, 'AF_UNIX': <AddressFamily(_value_=1, _name_='AF_UNIX', __objclass__=<...>) at remote 0x7f29c14d7280>, 'AF_INET': <AddressFamily(_value_=...(truncated), tstate=0x56171a7f7a10)
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/internal/pycore_ceval.h:40
#18 function_code_fastcall (tstate=0x56171a7f7a10, co=<optimized out>, args=<optimized out>, nargs=1, globals=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Objects/call.c:330
#19 0x00007f29c1bd8ac0 in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=<optimized out>, args=0x7f29c1705940, callable=<function at remote 0x7f29c125a4c0>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:118
#20 PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x7f29c1705940, callable=<function at remote 0x7f29c125a4c0>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/cpython/abstract.h:127
#21 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:5077
#22 _PyEval_EvalFrameDefault (tstate=<optimized out>, f=<optimized out>, throwflag=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:3489
#23 0x00007f29c1bd29e5 in _PyEval_EvalFrame (throwflag=0, f=Frame 0x7f29c17057c0, for file <string>, line 1, in <module> (), tstate=0x56171a7f7a10) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Include/internal/pycore_ceval.h:40
#24 _PyEval_EvalCode (tstate=<optimized out>, _co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x0, kwcount=0, kwstep=2, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0)
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:4329
#25 0x00007f29c1c4cda5 in _PyEval_EvalCodeWithName (_co=<optimized out>, globals=<optimized out>, 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, args=<optimized out>, 
    argcount=<optimized out>, kwnames=<optimized out>, kwargs=0x0, kwcount=0, kwstep=2, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:4361
#26 0x00007f29c1c4cd3d in PyEval_EvalCodeEx (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0)
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:4377
#27 0x00007f29c1c4ccef in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/ceval.c:828
#28 0x00007f29c1c7d2b4 in run_eval_code_obj (tstate=0x56171a7f7a10, co=0x7f29c16c3b30, 
    globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>})
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/pythonrun.c:1221
#29 0x00007f29c1c79116 in run_mod (mod=<optimized out>, filename=<optimized out>, 
    globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, 
    flags=<optimized out>, arena=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/pythonrun.c:1242
#30 0x00007f29c1c6fdf8 in PyRun_StringFlags (str=<optimized out>, start=257, 
    globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x56171a808950>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7f29c1717ae0>, 'DNSUtils': <type at remote 0x56171a94de00>}, flags=0x7ffd53933bb8)
    at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/pythonrun.c:1108
#31 0x00007f29c1c6fac0 in PyRun_SimpleStringFlags (command=0x7f29c1692cd0 "from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.dnsToIp(\"fail2ban_01\"))\n", flags=0x7ffd53933bb8) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Python/pythonrun.c:497
#32 0x00007f29c1c6f5a8 in pymain_run_command (cf=0x7ffd53933bb8, command=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Modules/main.c:246
#33 pymain_run_python (exitcode=0x7ffd53933bb0) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Modules/main.c:589
#34 Py_RunMain () at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Modules/main.c:677
#35 0x00007f29c1c3f6dd in Py_BytesMain (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/python3.9-3.9.16-1.el9.x86_64/Modules/main.c:731
#36 0x00007f29c18faeb0 in __libc_start_call_main (main=main@entry=0x56171a447160 <main>, argc=argc@entry=3, argv=argv@entry=0x7ffd53933de8) at ../sysdeps/nptl/libc_start_call_main.h:58
#37 0x00007f29c18faf60 in __libc_start_main_impl (main=0x56171a447160 <main>, argc=3, argv=0x7ffd53933de8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffd53933dd8) at ../csu/libc-start.c:389
#38 0x000056171a447095 in _start ()
(gdb) 

Conclusion of the chat
OK, so it is confirmed... you can try to fix this module manually (remove INET6 and IPv6 family), I'll try to change it too for new f2b release
but it is basically python's error, so basically to be fixed there

@ptempier ptempier added the bug label Jan 5, 2023
@sebres
Copy link
Contributor

sebres commented Jan 6, 2023

Well as already said rather a 3rd party issue (python's socket module is to blame here), but we can try to circumvent it also (for the case allowipv6 is off) and I will.
Just a bug report on python side would be useful too and is the only proper way to go.

@sebres
Copy link
Contributor

sebres commented Jan 6, 2023

Hmm... the issue here is if I'll try to restrict DNSUtils.dnsToIp() to consider the IPv6IsAllowed(), it will break auto-detection by allowipv6 = auto (because it is implemented using search of IPv6 in call of getSelfIPs() that does DNSUtils.dnsToIp() for the local host name.
And reimplementation of that logic is difficult at the moment (till we support many python versions and platforms), see #3132 (comment)
But unless it gets implemented in some other way (either the auto-detection of IPv6-support or the getSelfIPs()), it'll be impossible to circumvent that segfault on side of DNSUtils.dnsToIp().
The snake quasi biting itself into the tail.
But I could do that only for the case allowipv6 = no (manually disabled in fail2ban configuration without auto-detection).
WiP.

@ptempier
Copy link
Author

ptempier commented Jan 6, 2023

Ticket open : python/cpython#100795

@sebres
Copy link
Contributor

sebres commented Jan 6, 2023

OK, 58834b6 must "fix" that, well basically circumvent the segfault (I also found better way to auto-detect allowipv6 = auto without the necessity to iterate over all IPs)...

You can either update fail2ban/server/ipdns.py in python's modules under fail2ban manually (it should be compatible to 1.0.1 too), but do 2to3 firstly for the file (see example below)...
Or you can try to install fail2ban from source, see in our wiki.

At least you can give it an attempt without to install it... Try to do this in your docker where that was reproducible previously:

cd /tmp
git clone -b fix-gh-3438 --single-branch https://github.com/sebres/fail2ban.git /tmp/f2b-fix-gh-3438/
# or download and unpack https://github.com/sebres/fail2ban/archive/refs/heads/fix-gh-3438.zip
cd /tmp/f2b-fix-gh-3438
./fail2ban-2to3
PYTHONPATH=. python3 -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.IPv6IsAllowed(), DNSUtils.dnsToIp("localhost"))'
PYTHONPATH=. python3 -c 'from fail2ban.server.ipdns import DNSUtils; print(DNSUtils.IPv6IsAllowed(), DNSUtils.dnsToIp("fail2ban_01"))'

The results must be something like (False, {'127.0.0.1'}), and of course no segmentation fault anymore

@sebres sebres changed the title [BR]: As discussed on irc with sebres , fail2ban crash on host with ipv6 disabled. [BR]: socket.getaddrinfo segfaults, fail2ban crash on host with ipv6 disabled. Jan 6, 2023
@sebres
Copy link
Contributor

sebres commented Jan 9, 2023

@ptempier Are there any new information?

@sebres
Copy link
Contributor

sebres commented Jan 9, 2023

As further discussed on IRC, even disabling it in kernel by start doesn't avoid socket.getaddrinfo from segfaulting.
Although in that case bind of AF_INET6 socket to :: fails as expected (net.ipv6.conf.all.disable_ipv6 = 1 isn't enough to fail), so to fix auto-detection of IPv6 availability, I extended it now in d8a9812 to consider net.ipv6.conf.all.disable_ipv6 (will be preferred if exists).

Since implementation of #3132 belongs to almost the same subject and may also help against this issue, I'll try to put my test branch iterating over addresses of interfaces (with libc.getifaddrs using ctypes.CDLL, Linux only) to the solution.

sebres added a commit to sebres/fail2ban that referenced this issue Jan 11, 2023
@sebres sebres closed this as completed in 58834b6 Jan 11, 2023
sebres added a commit that referenced this issue Jan 11, 2023
* circumvent SEGFAULT in a python's socket module by getaddrinfo with disabled IPv6 (gh-3438)
* improve auto-detection of IPv6 support (`allowipv6 = auto` by default)
* improve `ignoreself` by considering all local addresses from network interfaces additionally to IPs from hostnames (gh-3132)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants