This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix NPTL cancellation and longjmp with sigaltstack


Hi!

tst-cancelx20.c sometimes fails.  It appears that it works if the alternate
stack happens to be allocated below thread's stack, but doesn't work
if it is above it, as both unwind.c and pt-longjmp.c compare stack pointers
and assume that greater means outer (on _STACK_GROWS_DOWN arches, that is,
but NPTL only supports them ATM), but with inner alternate stack above the
outer original stack this assumption is broken.
The following patch adjusts all pointers used in stack (and backing store)
pointer comparisons so that the original stack is always above the alternate
stack.

While going through bits/setjmp.h _JMPBUF_UNWINDS definitions (because all
of them had to be copied/adjusted for the new argument), I stopped on
sh and sparc32 _JMPBUF_UNWINDS definitions.
sysdeps/sh/bits/setjmp.h has:
#define _JMPBUF_UNWINDS(jmpbuf, address) \
  ((void *) (address) < &(jmpbuf)[0].__regs[7])
The & there is really weird, there is no guarantee the jmp_buf will be on
the stack, I'd think that if __regs[7] is saved stack pointer it must be
(jmpbuf)[0].__regs[7].
sysdeps/sparc/sparc32/bits/setjmp.h has:
typedef int __jmp_buf[3];
...
#define _JMPBUF_UNWINDS(jmpbuf, address) \
  ((int) (address) < (jmpbuf)[JB_SP])
Signed comparison is plain wrong here.  Wonder how it ever worked.

2003-12-18  Jakub Jelinek  <jakub@redhat.com>

	* unwind.c (FRAME_LEFT): Add adj argument.  Subtract it from each
	comparison operand.
	(unwind_stop): Use _JMPBUF_CFA_UNWINDS_ADJ macro instead of
	_JMPBUF_CFA_UNWINDS.  Adjust FRAME_LEFT invocations.
	* pt-longjmp.c: Include jmpbuf-unwind.h.
	(__pthread_cleanup_upto): Use _JMPBUF_UNWINDS_ADJ macro instead of
	_JMPBUF_UNWINDS.  Adjust compared pointers.
	* init.c (__pthread_initialize_minimal_internal): Initialize
	pd->stackblock_size.
	* sysdeps/pthread/jmpbuf-unwind.h: Removed.
	* sysdeps/alpha/jmpbuf-unwind.h: New file.
	* sysdeps/i386/jmpbuf-unwind.h: New file.
	* sysdeps/powerpc/jmpbuf-unwind.h: New file.
	* sysdeps/s390/jmpbuf-unwind.h: New file.
	* sysdeps/sh/jmpbuf-unwind.h: New file.
	* sysdeps/sparc/sparc32/jmpbuf-unwind.h: New file.
	* sysdeps/x86_64/jmpbuf-unwind.h: New file.
	* sysdeps/ia64/jmpbuf-unwind.h: Include stdint.h.
	(_JMPBUF_CFA_UNWINDS): Remove.
	(_JMPBUF_CFA_UNWINDS, _JMPBUF_UNWINDS_ADJ): Define.

--- libc/nptl/unwind.c.jj	2003-12-12 01:05:07.000000000 +0100
+++ libc/nptl/unwind.c	2003-12-18 15:03:19.000000000 +0100
@@ -29,9 +29,11 @@
 #ifdef HAVE_FORCED_UNWIND
 
 #ifdef _STACK_GROWS_DOWN
-# define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
+# define FRAME_LEFT(frame, other, adj) \
+  ((uintptr_t) frame - adj >= (uintptr_t) other - adj)
 #elif _STACK_GROWS_UP
-# define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
+# define FRAME_LEFT(frame, other, adj) \
+  ((uintptr_t) frame - adj <= (uintptr_t) other - adj)
 #else
 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
 #endif
@@ -46,6 +48,11 @@ unwind_stop (int version, _Unwind_Action
   struct pthread *self = THREAD_SELF;
   struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup);
   int do_longjump = 0;
+  
+  /* Adjust all pointers used in comparisons, so that top of thread's
+     stack is at the top of address space.  Without that, things break
+     if stack is allocated above the main stack.  */
+  uintptr_t adj = (uintptr_t) self->stackblock + self->stackblock_size;
 
   /* Do longjmp if we're at "end of stack", aka "end of unwind data".
      We assume there are only C frame without unwind data in between
@@ -53,7 +60,8 @@ unwind_stop (int version, _Unwind_Action
      of a function is NOT within it's stack frame; it's the SP of the
      previous frame.  */
   if ((actions & _UA_END_OF_STACK)
-      || ! _JMPBUF_CFA_UNWINDS  (buf->cancel_jmp_buf[0].jmp_buf, context))
+      || ! _JMPBUF_CFA_UNWINDS_ADJ (buf->cancel_jmp_buf[0].jmp_buf, context,
+				    adj))
     do_longjump = 1;
 
   if (__builtin_expect (curp != NULL, 0))
@@ -64,7 +72,7 @@ unwind_stop (int version, _Unwind_Action
       struct _pthread_cleanup_buffer *oldp = buf->priv.data.cleanup;
       void *cfa = (void *) _Unwind_GetCFA (context);
 
-      if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp)))
+      if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp, adj)))
 	{
 	  do
 	    {
@@ -77,7 +85,8 @@ unwind_stop (int version, _Unwind_Action
 	      /* To the next.  */
 	      curp = nextp;
 	    }
-	  while (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp)));
+	  while (curp != oldp
+		 && (do_longjump || FRAME_LEFT (cfa, curp, adj)));
 
 	  /* Mark the current element as handled.  */
 	  THREAD_SETMEM (self, cleanup, curp);
--- libc/nptl/pt-longjmp.c.jj	2003-08-08 09:42:48.000000000 +0200
+++ libc/nptl/pt-longjmp.c	2003-12-18 15:03:37.000000000 +0100
@@ -20,7 +20,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include "pthreadP.h"
-
+#include "jmpbuf-unwind.h"
 
 void
 __pthread_cleanup_upto (__jmp_buf target, char *targetframe)
@@ -28,18 +28,24 @@ __pthread_cleanup_upto (__jmp_buf target
   struct pthread *self = THREAD_SELF;
   struct _pthread_cleanup_buffer *cbuf;
 
+  /* Adjust all pointers used in comparisons, so that top of thread's
+     stack is at the top of address space.  Without that, things break
+     if stack is allocated above the main stack.  */
+  uintptr_t adj = (uintptr_t) self->stackblock + self->stackblock_size;
+  uintptr_t targetframe_adj = (uintptr_t) targetframe - adj;
+
   for (cbuf = THREAD_GETMEM (self, cleanup);
-       cbuf != NULL && _JMPBUF_UNWINDS (target, cbuf);
+       cbuf != NULL && _JMPBUF_UNWINDS_ADJ (target, cbuf, adj);
        cbuf = cbuf->__prev)
     {
 #if _STACK_GROWS_DOWN
-      if ((char *) cbuf <= targetframe)
+      if ((uintptr_t) cbuf - adj <= targetframe_adj)
         {
           cbuf = NULL;
           break;
         }
 #elif _STACK_GROWS_UP
-      if ((char *) cbuf >= targetframe)
+      if ((uintptr_t) cbuf - adj >= targetframe_adj)
         {
           cbuf = NULL;
           break;
--- libc/nptl/init.c.jj	2003-12-18 09:42:31.000000000 +0100
+++ libc/nptl/init.c	2003-12-18 14:53:19.000000000 +0100
@@ -220,6 +220,14 @@ __pthread_initialize_minimal_internal (v
   THREAD_SETMEM (pd, cpuclock_offset, GL(dl_cpuclock_offset));
 #endif
 
+  /* Defined in ld.so.  */
+  extern void *__libc_stack_end;
+
+  /* Set initial thread's stack block from 0 up to __libc_stack_end.
+     It will be bigger than it actually is, but for unwind.c/pt-longjmp.c
+     purposes this is good enough.  */
+  THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end);
+
   /* Initialize the list of all running threads with the main thread.  */
   INIT_LIST_HEAD (&__stack_user);
   list_add (&pd->list, &__stack_user);
--- libc/nptl/sysdeps/alpha/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/alpha/jmpbuf-unwind.h	2003-12-18 13:46:05.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[JB_SP] - (_adj))
--- libc/nptl/sysdeps/i386/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/i386/jmpbuf-unwind.h	2003-12-18 13:47:17.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[JB_SP] - (_adj))
--- libc/nptl/sysdeps/powerpc/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/powerpc/jmpbuf-unwind.h	2003-12-18 13:48:44.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[JB_GPR1] - (_adj))
--- libc/nptl/sysdeps/pthread/jmpbuf-unwind.h.jj	2003-09-04 07:42:24.000000000 +0200
+++ libc/nptl/sysdeps/pthread/jmpbuf-unwind.h	2003-12-18 15:03:40.000000000 +0100
@@ -1,24 +0,0 @@
-/* Copyright (C) 2003 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-#include <setjmp.h>
-#include <unwind.h>
-
-#define _JMPBUF_CFA_UNWINDS(_jmpbuf, _context) \
-  _JMPBUF_UNWINDS (_jmpbuf, (void *) _Unwind_GetCFA (_context))
--- libc/nptl/sysdeps/s390/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/s390/jmpbuf-unwind.h	2003-12-18 13:49:58.000000000 +0100
@@ -0,0 +1,29 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj)	 \
+  ((uintptr_t) (_address) - (_adj)			 \
+   < (uintptr_t) (_jmpbuf)->__gregs[__JB_GPR15] - (_adj))
--- libc/nptl/sysdeps/sh/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/sh/jmpbuf-unwind.h	2003-12-18 13:52:14.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(jmpbuf, address, adj) \
+  ((uintptr_t) (address) - (adj) < (uintptr_t) (_jmpbuf)[0].__regs[7] - (adj))
--- libc/nptl/sysdeps/sparc/sparc32/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/sparc/sparc32/jmpbuf-unwind.h	2003-12-18 13:55:03.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[JB_SP] - (_adj))
--- libc/nptl/sysdeps/unix/sysv/linux/ia64/jmpbuf-unwind.h.jj	2003-09-04 07:43:26.000000000 +0200
+++ libc/nptl/sysdeps/unix/sysv/linux/ia64/jmpbuf-unwind.h	2003-12-18 13:45:22.000000000 +0100
@@ -18,12 +18,16 @@
    02111-1307 USA.  */
 
 #include <setjmp.h>
+#include <stdint.h>
 #include <unwind.h>
 
-#define _JMPBUF_CFA_UNWINDS(_jmpbuf, _context) \
-  ({ void *_cfa = (void *) _Unwind_GetCFA (_context);			\
-     (_cfa < (void *)(((long *)(_jmpbuf))[0])				\
-      || (_cfa == (void *)(((long *)(_jmpbuf))[0])			\
-	  && (void *) _Unwind_GetBSP (_context)				\
-	     >= (void *)(((long *)(_jmpbuf))[17])));			\
+#define _JMPBUF_CFA_UNWINDS(_jmpbuf, _context, _adj) \
+  ({ uintptr_t _cfa = (uintptr_t) _Unwind_GetCFA (_context) - (_adj);	\
+     (_cfa < (uintptr_t)(((long *)(_jmpbuf))[0]) - (_adj)		\
+      || (_cfa == (uintptr_t)(((long *)(_jmpbuf))[0]) - (_adj)		\
+	  && (uintptr_t) _Unwind_GetBSP (_context) - (_adj)		\
+	     >= (uintptr_t)(((long *)(_jmpbuf))[17]) - (_adj)));	\
   })
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t)(_address) - (_adj) < (uintptr_t)(((long *)_jmpbuf)[0]) - (_adj))
--- libc/nptl/sysdeps/x86_64/jmpbuf-unwind.h.jj	2003-12-18 13:34:48.000000000 +0100
+++ libc/nptl/sysdeps/x86_64/jmpbuf-unwind.h	2003-12-18 13:55:47.000000000 +0100
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[JB_RSP] - (_adj))

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]