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