[sane-devel] Segmentation faults in several backends
Henning Meier-Geinitz
henning@meier-geinitz.de
Sat, 22 Feb 2003 18:01:31 +0100
--EVF5PPMfhYS0aIcm
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hi,
While trying to reproduce the problems with the net backend reported
some days ago I found some more backends that cause segfaults when
called more then once. By calling the backends more then once I mean:
sane_init
sane_get_device
[... other sane functions]
sane_exit
sane_init
sane_get_device
[... other sane functions]
sane_exit
[and so on]
Some backends don't initialize global variables in sane_init. If they
check for 0 afterwards, this test will be ok for the first run but not
for the second sane_init. So global variables must be initialized
explicitley in sane_init (or set to 0 in sane_exit). It's not enough
to just write
int some_global_variable = 0;
because this initialization will be done only when the library was
loaded, not with every call of sane_init.
Usually variables like devlist, num_devices, first_device or
first_handle are the culprits. Please, everyone check if those are
initialized correctly. I guess that most backends don't initialize
them correctly.
With the usual setup you'll never notice the segfault because the
frontends call sane_init only once and never again. Further more, the
default is to link to libsane-dll and this backend unloads the library
in sane_exit. But there are reasons to link to backends directly so
these bugs should be fixed.
I'll attach a test program that calls the following sequence ten times:
sane_init
sane_get_devices
sane_open
sane_close
sane_exit
Compile with "gcc -o sane-test sane-test.c -ldl".
It loads every SANE library that exists in /usr/local/lib/sane
manually and will report errors and segmentation faults.
Please check with your backend. Some segfaults only occur when devices
are connected. Some backends segfault even without any devices:
testing libsane-artec.so: 0 1 2 got signal 11
testing libsane-mustek_pp.so: 0 1 got signal 11
testing libsane-pie.so: 0 1 2 got signal 11
testing libsane-umax.so: 0 1 2 got signal 11
testing libsane-umax_pp.so: 0 1 got signal 11
Bye,
Henning
--EVF5PPMfhYS0aIcm
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="sane-test.c"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
#include <sane/sane.h>
#define SANE_LIB_DIR "/usr/local/lib/sane/"
enum SANE_Ops
{
OP_INIT = 0,
OP_EXIT,
OP_GET_DEVICES,
OP_OPEN,
OP_CLOSE,
OP_STRSTATUS,
NUM_OPS
};
static const char *op_name[] = {
"sane_init", "sane_exit", "sane_get_devices", "sane_open", "sane_close", "sane_strstatus"
};
static void *(*op[NUM_OPS]) ();
static int got_signal = 0;
static void
sig_handler (int signal)
{
fprintf (stderr, "got signal %d\n", signal);
_exit (1);
}
int main ()
{
struct stat stat_buf;
DIR *dir;
struct dirent *dir_entry;
struct sigaction act;
pid_t pid;
if (stat (SANE_LIB_DIR, &stat_buf) < 0)
{
fprintf (stderr, "can't stat %s: %s\n", SANE_LIB_DIR, strerror (errno));
return 1;
}
if (!S_ISDIR (stat_buf.st_mode))
{
fprintf (stderr, "%s is not a directory\n", SANE_LIB_DIR);
return 1;
}
if ((dir = opendir (SANE_LIB_DIR)) == 0)
{
fprintf (stderr, "cannot read directory %s: %s\n", SANE_LIB_DIR, strerror (errno));
return 1;
}
memset (&act, 0, sizeof (act));
act.sa_handler = sig_handler;
sigaction (SIGSEGV, &act, 0);
while ((dir_entry = readdir (dir)) != 0)
{
char libpath[PATH_MAX];
void *dl_handle;
int i;
SANE_Status status;
SANE_Int version_code;
const SANE_Device **device_list;
got_signal = 0;
if ((strlen (dir_entry->d_name) < strlen ("libsane.so"))
|| (strncmp ("libsane", dir_entry->d_name, strlen ("libsane")) != 0)
|| (strncmp (dir_entry->d_name + strlen (dir_entry->d_name) - 3, ".so", 3) != 0))
continue;
fprintf (stderr, "testing %s: ", dir_entry->d_name);
sprintf (libpath, "%s%s", SANE_LIB_DIR, dir_entry->d_name);
dl_handle = dlopen (libpath, RTLD_LAZY);
if (!dl_handle)
{
fprintf (stderr, "opening %s failed: %s\n", libpath, dlerror());
continue;
}
for (i = 0; i < NUM_OPS; i++)
{
op[i] = (void *(*)(void)) dlsym (dl_handle, op_name[i]);
if (!op[i])
break;
}
if (i < NUM_OPS)
{
fprintf (stderr, "getting symbol %s failed: %s\n", op_name[i], dlerror());
dlclose (dl_handle);
continue;
}
pid = fork ();
if (pid == 0)
{
for (i = 0; i < 10; i++)
{
SANE_Handle handle;
fprintf (stderr, "%d ", i);
status = (SANE_Status) (*op[OP_INIT]) (&version_code, 0);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "sane_init failed (i = %d): %s\n", i,
(SANE_String_Const) (*op[OP_STRSTATUS]) (status));
break;
}
status = (SANE_Status) (*op[OP_GET_DEVICES]) (&device_list, SANE_FALSE);
if (status != SANE_STATUS_GOOD)
{
fprintf (stderr, "sane_get_devices failed (i = %d): %s\n", i,
(SANE_String_Const) (*op[OP_STRSTATUS]) (status));
break;
}
status = (SANE_Status) (*op[OP_OPEN]) ("", &handle);
if (status == SANE_STATUS_GOOD)
(*op[OP_CLOSE]) (handle);
(*op[OP_EXIT]) ();
}
if (i < 10)
{
dlclose (dl_handle);
_exit (2);
}
_exit (0);
}
else if (pid > 0)
{
int status;
waitpid (pid, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
fprintf (stderr, "ok\n");
dlclose (dl_handle);
}
}
fprintf (stderr, "done\n");
return 0;
}
--EVF5PPMfhYS0aIcm--