rlm_python and dynload problem

Aurélien Geron aureliengeron.freeradius at wifirst.fr
Tue Apr 27 01:56:22 CEST 2010


Hi,

I came across a bug when rlm_python executes python code that tries to load a dynamic (shared) module.  This bug seems to have been discussed 2 or 3 times on this list, but no really satisfying solution appears to have been found so far (as far as I know), so I thought I might raise the subject again and perhaps try to contribute in finding a solution.

In short, I think the solution to this problem is explained here, but I don't know how to implement it in freeRADIUS : http://docs.python.org/release/2.5.2/ext/link-reqs.html

Here is my setup :
- running a perfectly standard Debian Lenny (2.6.26-2-amd64)
- installed the latest freeradius package from the lenny-backports (2.1.8+dfsg-1~bpo50+1)
- the python debian package is the one installed with Debian Lenny (2.5.2-3)

Here is my config:

----------------------------------------------
#######
# /etc/freeradius/modules/python
#######
python python_test {
  mod_instantiate = radiusd_test
  func_instantiate = instantiate
}

#######
# /etc/freeradius/radiusd.conf
#######
...
instantiate {
    python_test
}
...

#######
# /usr/lib/python2.5/site-packages/radiusd_test.py
#######
def instantiate(p):
    radiusd.radlog(radiusd.L_DBG, "THIS WORKS")
    import random  # this crashes
    radiusd.radlog(radiusd.L_DBG, "THIS IS NEVER REACHED !")
----------------------------------------------

Here is the output of "freeradius -X" :

----------------------------------------------
FreeRADIUS Version 2.1.8, for host x86_64-pc-linux-gnu, built on Apr 26 2010 at 21:49:28
Copyright (C) 1999-2009 The FreeRADIUS server project and contributors. 
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. 
You may redistribute copies of FreeRADIUS under the terms of the 
GNU General Public License v2. 
Starting - reading configuration files ...
including configuration file /etc/freeradius/radiusd.conf
...
 Module: Linked to module rlm_python
 Module: Instantiating python_test
python_init done
  python python_test {
	mod_instantiate = "radiusd_test"
	func_instantiate = "instantiate"
  }
THIS WORKS
rlm_python:EXCEPT:<type 'exceptions.ImportError'>: /usr/lib/python2.5/lib-dynload/math.so: undefined symbol: PyExc_ValueError
/etc/freeradius/modules/python[24]: Instantiation failed for module "python_test"
----------------------------------------------

The module is properly loaded, the "instantiate" function gets called, and the first log message is output.  In fact, any 100% pure python code works fine.  The bug happens when a python module gets loaded dynamically : importing any module located in /usr/lib/python2.5/lib-dynload/*.so will crash.

>From what I understand, here's what happens:
1) rlm_python was built against libpython2.5.a, but for optimization purposes, the linker stripped out all the symbols that were not used by rlm_python itself (including, for example, PyExc_ValueError). This behavior is platform-dependent, which probably explains why everything works fine on centos, for example.
2) when the radiusd_test module runs, it tries to import the "random" module, which itself tries to import the "math" module, which is in lib-dynload, and gets loaded dynamically. 
3) unfortunately, python fails to load that module because it uses the PyExc_ValueError symbol and it does not know where it is defined (it is located in /usr/lib/libpython2.5.so.1, but unfortunately, the "math" module does not know that.


As I said, I believe that the solution to this problem is clearly explained here: http://docs.python.org/release/2.5.2/ext/link-reqs.html
As it's just a few paragraphs, I'll quote it here, for your convenience:

----------------------------------------------
While the configure script shipped with the Python sources will correctly build Python to export the symbols needed by dynamically linked extensions, this is not automatically inherited by applications which embed the Python library statically, at least on Unix. This is an issue when the application is linked to the static runtime library (libpython.a) and needs to load dynamic extensions (implemented as.so files).
The problem is that some entry points are defined by the Python runtime solely for extension modules to use. If the embedding application does not use any of these entry points, some linkers will not include those entries in the symbol table of the finished executable. Some additional options are needed to inform the linker not to remove these symbols.
Determining the right options to use for any given platform can be quite difficult, but fortunately the Python configuration already has those values. To retrieve them from an installed Python interpreter, start an interactive interpreter and have a short session like this:
>>> import distutils.sysconfig
>>> distutils.sysconfig.get_config_var('LINKFORSHARED')
'-Xlinker -export-dynamic'
The contents of the string presented will be the options that should be used. If the string is empty, there's no need to add any additional options. The LINKFORSHARED definition corresponds to the variable of the same name in Python's top-level Makefile.
----------------------------------------------

I think the short-term solution to this bug is to modify "src/modules/rlm_python/configure.in" and make it use the LINKFORSHARED linker options when linking rlm_python against libpython2.5.a., This could be done by adding a line such as:
                      PY_OTHER_LDFLAGS=`sed -n -e 's/^LINKFORSHARED=\(.*\)/\1/p' $PY_MAKEFILE`
just under this one:
                      PY_OTHER_LIBS=`sed -n -e 's/^LIBS=\(.*\)/\1/p' $PY_MAKEFILE`
and then using $PY_OTHER_LDFLAGS somewhere... the thing is, I have no idea where, because I am new to all these dynamic modules and embedded interpreters . I tried this though:
                        python_ldflags="$PY_LIB_LOC $PY_EXTRA_LIBS $PY_OTHER_LDFLAGS -lpython${PY_VERSION} -lm"
and recompiled the debian package (dpkg-buildpackage), but the problem was not fixed.
Can someone tell me where to add those options exactly ?  I can try recompiling as many times as necessary.

Apparently, at least one person complains about the fact that you have to link against the static python runtime library (libpython2.5.a) even when you plan to use dynamic modules, and he has opened a bug report here:
http://bugs.python.org/issue4434

Basically, if I understand correctly, his idea is to have the python fellows declare the proper dependencies in every *.so file, so that the libpython2.5.so.1 file gets loaded automatically when the "math" module (or any other dynamic module) gets loaded.  Maybe that's the ideal solution, I really don't know.  But it seems to me that we should try to  fix freeRADIUS so that it works around this bug before python dependencies are fixed (it make take a while or even never happen).  So I thing the only short-medium-term solution is to use LINKFORSHARED linker options.

Thanks for reading this huge message. I hope we can beat this bug.

Aurelien Geron



More information about the Freeradius-Users mailing list