Re: Netscape buggy, kernel OK - some test results

Stanislav Meduna (stano@trillian.eunet.sk)
Sat, 23 Jan 1999 19:00:24 +0100 (CET)


> I'll try to polish my hack a bit - seems like
> it can help more people.

Those who have problem with freezing netscape,
please try the following code. If it helps, we
are sure that the kernel is not the problem.

It works for me with 2.2.0-final and Netscape 4.5,
I have not tested it very carefully and I don't
guarantee it works anywhere else.

Thanks
Stano

========
#include <dlfcn.h>
#include <unistd.h>
#include <limits.h>

/* This is a netscape bug workaround. Search for a pipe,
where only 1 byte strings \0372 are written to the write
end and only 1 byte strings are requested back. Count
the difference and fake any writes that would cause
an overflow of the pipe and corresponding reads.

This is a really dirty hack with almost no error checking
and relying on the netscape's behaviour.

Compile with
gcc -c nspipepatch.c
ld -shared -o libnspipepatch.so -ldl nspipepatch.o

and add
LD_PRELOAD=<path>/libnspipepatch.so

to your netscape starting script.

(C) 1999 Stanislav Meduna <stano@trillian.eunet.sk>
There is absolutely NO WARRANTY. The use and distribution
is completely free
*/

/* The magic netscape char flowing through the pipe */
#define MAGIC ((unsigned char) '\372')

/* libc handle for dlsym */
static void *dlhdl=0;

/* pointers to overloaded functions */
static int (*pipe_fp)(int [2])=0;
static ssize_t (*write_fp)(int, const void *, size_t)=0;
static ssize_t (*read_fp)(int, void *, size_t)=0;
static int (*close_fp)(int)=0;

/* we keep the info about our descriptors here */
static struct nshack_pipe_t
{
/* If this is a pipe that could be our one,
mark both ends here. If we shouldn't touch
the descriptor, enter -1 here
*/
int read_end, write_end;

/* The difference between writes and reads,
maintained on the write end
*/
int diff;

/* Number of faked writes, maintained on the write end */
int faked;
} fdflags[256];

/* Mark the descriptors as unused or not of our interest */
static void clearfd(int fd)
{
fdflags[fd].read_end = fdflags[fd].write_end = -1;
fdflags[fd].diff = fdflags[fd].faked = 0;
}

/* Clear both ends */
static void clearboth(int fd)
{
int rd = fdflags[fd].read_end;
int wr = fdflags[fd].write_end;

if (rd >= 0)
clearfd(rd);
if (wr >= 0)
clearfd(wr);
}

/* Init the pointers to overloaded functions */
static void initptrs()
{
int i;

dlhdl = dlopen("/lib/libc.so.6", RTLD_NOW);

if (! dlhdl)
exit(99);

pipe_fp = dlsym(dlhdl, "pipe");
write_fp = dlsym(dlhdl, "write");
read_fp = dlsym(dlhdl, "read");
close_fp = dlsym(dlhdl, "close");

if (! pipe_fp || ! write_fp || ! read_fp || ! close_fp)
exit(99);

for (i=0; i < sizeof(fdflags)/sizeof(fdflags[0]); i++)
clearfd(i);
}

/* Unload the library */
void _fini()
{
if (dlhdl)
dlclose(dlhdl);
}

/* Overloaded pipe - mark the fresh pipes */
int pipe(int fd[2])
{
int res;

if (! pipe_fp)
initptrs();

res = (*pipe_fp)(fd);

if (! res)
{
/* This could be our pipe - watch it during read/write */
fdflags[fd[0]].read_end = fdflags[fd[1]].read_end = fd[0];
fdflags[fd[0]].write_end = fdflags[fd[1]].write_end= fd[1];
fdflags[fd[0]].diff = fdflags[fd[1]].faked = 0;
}

return res;
}

/* Overloaded write */
ssize_t write(int fd, const void *buf, size_t cnt)
{
int res=0;

if (! write_fp)
initptrs();
if (cnt != 1 || *(unsigned char *)buf != MAGIC)
clearboth(fd); /* This is not our pipe - magic not seen */

if (fd == fdflags[fd].write_end)
{
/* This is the write end of a watched pipe */
fdflags[fd].diff++;

/* Play safe - reduce the allowed difference
to guard against off-by-one errors and
similar :-)
*/
if (fdflags[fd].diff >= PIPE_BUF-64)
{
/* We fake the write */
fdflags[fd].faked++;
res = 1;
}
else
res = (*write_fp)(fd, buf, cnt);
}
else
res = (*write_fp)(fd, buf, cnt);

return res;
}

/* Overloaded read */
ssize_t read(int fd, void *buf, size_t cnt)
{
int res;
int watch=0;

if (! read_fp)
initptrs();

if (fd == fdflags[fd].read_end)
{
if (cnt == 1)
watch = 1;
else
clearboth(fd); /* The true one always wants one char only */

/* This is the read end of a watched pipe */
if (watch && fdflags[fdflags[fd].write_end].faked)
{
/* Faked writes exist - compensate with faked reads */
fdflags[fdflags[fd].write_end].faked--;
fdflags[fdflags[fd].write_end].diff--;
*((unsigned char *) buf) = MAGIC;
return 1;
}
}

/* Do the real read */
res = (*read_fp)(fd, buf, cnt);

if (watch && res == 1)
{
/* If we watch the pipe and the read was successfull,
use the read value to make sure this is THE pipe.
*/
if (*((unsigned char *) buf) == MAGIC)
fdflags[fd].diff--;
else
clearboth(fd);
}

return res;
}

/* Overloaded close (we want to be at least a bit correct) */
int close(int fd)
{
if (! close_fp)
initptrs();

if (fd == fdflags[fd].read_end || fd == fdflags[fd].write_end)
clearboth(fd);

return (*close_fp)(fd);
}
========

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/