Mrznuci netscape - patch
Stanislav Meduna
stano na trillian.eunet.sk
Neděle Leden 24 15:17:42 CET 1999
Zdravim,
mozno sa niektorym z vas stava, ze pri nacitani
niektorych stranok netscape scape :-) a treba
ho odstrelit cez kill -9 alebo cez destroy.
Zda sa, ze sa mi podarilo diagnostikovat pricinu -
netscape pouziva pre nejaku internu signalizaciu
pipu, ktoru bohuzial neobsluhuje ani cez select,
ani cez nonblocking operacie, ale natvrdo a nevie
prezit jej zaplnenie. Pri mojich testoch vyzadoval
pipu, ktora je schopna nabuffrovat vyse 17 kB -
velkost v Linuxe je 4 kB a POSIX-om vyzadovane
minimum myslim 512 bytes).
Pocas patrania po pricinach som napisal malu
LD_PRELOAD kniznicu, ktora odchyti konkretnu
pipu a simuluje zapisy a citania, pokial usudi,
ze hrozi zaplnenie. Viacerym uzivatelom
linux-kernel listu funguje. Prosim tych, co maju
tieto problemy, aby vyskusali nasledujuci
kod, komu sa chce, nech ho aj pozrie kritickym
okom, a dali mi vediet vysledok. Je to strasna
prasacina, ale to uz je mojim zvykom :-)
Pouzitie je v hlavicke kodu. Pokial niekto pouziva
Netscape s libc5, treba zmenit v kode libc.6 na
libc.5 a linkovat s libdl.so.1:
ld -shared -o libnspipepatch.so /lib/libdl.so.1 nspipepatch.o
Pre blizsiu diskusiu problemu hladajte v archive
linux-kernel thread "Netscape buggy, kernel OK -
some test results" a vobec vsetko, co ma v subjecte
Netscape.
Stano
=== nspipepatch.c ===
#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 na 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);
}
=== nspipepatch.c ===
Další informace o konferenci Linux