From: Ram Pai <linuxram@us.ibm.com>
To: Florian Weimer <fweimer@redhat.com>
Cc: Dave Hansen <dave.hansen@intel.com>,
linux-api@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-mm@kvack.org
Subject: Re: pkeys: Reserve PKEY_DISABLE_READ
Date: Mon, 3 Dec 2018 22:23:18 -0800 [thread overview]
Message-ID: <20181204062318.GC11930@ram.oc3035372033.ibm.com> (raw)
In-Reply-To: <87pnuibobh.fsf@oldenburg.str.redhat.com>
On Mon, Dec 03, 2018 at 04:52:02PM +0100, Florian Weimer wrote:
> * Ram Pai:
>
> > So the problem is as follows:
> >
> > Currently the kernel supports 'disable-write' and 'disable-access'.
> >
> > On x86, cpu supports 'disable-write' and 'disable-access'. This
> > matches with what the kernel supports. All good.
> >
> > However on power, cpu supports 'disable-read' too. Since userspace can
> > program the cpu directly, userspace has the ability to set
> > 'disable-read' too. This can lead to inconsistency between the kernel
> > and the userspace.
> >
> > We want the kernel to match userspace on all architectures.
>
> Correct.
>
> > Proposed Solution:
> >
> > Enhance the kernel to understand 'disable-read', and facilitate architectures
> > that understand 'disable-read' to allow it.
> >
> > Also explicitly define the semantics of disable-access as
> > 'disable-read and disable-write'
> >
> > Did I get this right? Assuming I did, the implementation has to do
> > the following --
> >
> > On power, sys_pkey_alloc() should succeed if the init_val
> > is PKEY_DISABLE_READ, PKEY_DISABLE_WRITE, PKEY_DISABLE_ACCESS
> > or any combination of the three.
>
> Agreed.
>
> > On x86, sys_pkey_alloc() should succeed if the init_val is
> > PKEY_DISABLE_WRITE or PKEY_DISABLE_ACCESS or PKEY_DISABLE_READ
> > or any combination of the three, except PKEY_DISABLE_READ
> > specified all by itself.
>
> Again agreed. That's a clever way of phrasing it actually.
>
> > On all other arches, none of the flags are supported.
> >
> >
> > Are we on the same plate?
>
> I think so, thanks.
>
> Florian
Ok. here is a patch, compiled but not tested. See if this meets the
specifications.
-----------------------------------------------------------------------------------
commit 3dc06e73f3795921265d5d1d935e428deab01616
Author: Ram Pai <linuxram@us.ibm.com>
Date: Tue Dec 4 00:04:11 2018 -0500
pkeys: add support of PKEY_DISABLE_READ
Kernel supports 'disable-write' and 'disable-access'.
x86 cpu supports 'disable-write' and 'disable-access'. This
matches with the kernel support.
However POWER cpu supports 'disable-read' too. Since userspace can
program the cpu directly, userspace has the ability to set
'disable-read' too. This can lead to inconsistency between the kernel
and the userspace.
Make kernel match userspace on all architectures.
Enhance the kernel to understand 'disable-read', and facilitate architectures
that understand 'disable-read' to allow it.
Define the semantics of disable-access as 'disable-read and disable-write'
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h
index 20ebf15..4bd09d0 100644
--- a/arch/powerpc/include/asm/pkeys.h
+++ b/arch/powerpc/include/asm/pkeys.h
@@ -19,11 +19,7 @@
#define ARCH_VM_PKEY_FLAGS (VM_PKEY_BIT0 | VM_PKEY_BIT1 | VM_PKEY_BIT2 | \
VM_PKEY_BIT3 | VM_PKEY_BIT4)
-/* Override any generic PKEY permission defines */
#define PKEY_DISABLE_EXECUTE 0x4
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS | \
- PKEY_DISABLE_WRITE | \
- PKEY_DISABLE_EXECUTE)
static inline u64 pkey_to_vmflag_bits(u16 pkey)
{
@@ -199,6 +195,16 @@ static inline bool arch_pkeys_enabled(void)
return !static_branch_likely(&pkey_disabled);
}
+extern bool __arch_pkey_access_rights_valid(unsigned long rights);
+
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ if (static_branch_likely(&pkey_disabled))
+ return false;
+
+ return __arch_pkey_access_rights_valid(rights);
+}
+
extern void pkey_mm_init(struct mm_struct *mm);
extern bool arch_supports_pkeys(int cap);
extern unsigned int arch_usable_pkeys(void);
diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h
index 65065ce..e63bc37 100644
--- a/arch/powerpc/include/uapi/asm/mman.h
+++ b/arch/powerpc/include/uapi/asm/mman.h
@@ -30,10 +30,4 @@
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
-/* Override any generic PKEY permission defines */
-#define PKEY_DISABLE_EXECUTE 0x4
-#undef PKEY_ACCESS_MASK
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
- PKEY_DISABLE_WRITE |\
- PKEY_DISABLE_EXECUTE)
#endif /* _UAPI_ASM_POWERPC_MMAN_H */
diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
index 5d65c47..a4f288b 100644
--- a/arch/powerpc/mm/pkeys.c
+++ b/arch/powerpc/mm/pkeys.c
@@ -67,7 +67,7 @@ int pkey_initialize(void)
* Ensure that the bits a distinct.
*/
BUILD_BUG_ON(PKEY_DISABLE_EXECUTE &
- (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
+ (PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_ACCESS));
/*
* pkey_to_vmflag_bits() assumes that the pkey bits are contiguous
@@ -259,11 +259,20 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
new_amr_bits |= AMR_RD_BIT | AMR_WR_BIT;
else if (init_val & PKEY_DISABLE_WRITE)
new_amr_bits |= AMR_WR_BIT;
+ else if (init_val & PKEY_DISABLE_READ)
+ new_amr_bits |= AMR_RD_BIT;
init_amr(pkey, new_amr_bits);
return 0;
}
+bool __arch_pkey_access_rights_valid(unsigned long rights)
+{
+ unsigned long mask = PKEY_DISABLE_READ | PKEY_DISABLE_WRITE |\
+ PKEY_DISABLE_ACCESS;
+ return (rights & mask);
+}
+
void thread_pkey_regs_save(struct thread_struct *thread)
{
if (static_branch_likely(&pkey_disabled))
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index 19b137f..4f36a7e 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -14,6 +14,15 @@ static inline bool arch_pkeys_enabled(void)
return boot_cpu_has(X86_FEATURE_OSPKE);
}
+extern bool __arch_pkey_access_rights_valid(unsigned long rights);
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ if (!boot_cpu_has(X86_FEATURE_OSPKE))
+ return false;
+
+ return __arch_pkey_access_rights_valid(rights);
+}
+
/*
* Try to dedicate one of the protection keys to be used as an
* execute-only protection key.
diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c
index 6e98e0a..fcfe1b2 100644
--- a/arch/x86/mm/pkeys.c
+++ b/arch/x86/mm/pkeys.c
@@ -72,6 +72,17 @@ int __execute_only_pkey(struct mm_struct *mm)
return execute_only_pkey;
}
+bool __arch_pkey_access_rights_valid(unsigned long rights)
+{
+ unsigned long mask = PKEY_DISABLE_READ | PKEY_DISABLE_WRITE |\
+ PKEY_DISABLE_ACCESS;
+ if (!(rights & mask))
+ return false;
+
+ /* return failure if only PKEY_DISABLE_READ is specified */
+ return ((rights & mask) != PKEY_DISABLE_READ);
+}
+
static inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma)
{
/* Do this check first since the vm_flags should be hot */
diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
index 2955ba97..2c330fa 100644
--- a/include/linux/pkeys.h
+++ b/include/linux/pkeys.h
@@ -48,6 +48,11 @@ static inline void copy_init_pkru_to_fpregs(void)
{
}
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ return false;
+}
+
#endif /* ! CONFIG_ARCH_HAS_PKEYS */
#endif /* _LINUX_PKEYS_H */
diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h
index e7ee328..d2e1a5e 100644
--- a/include/uapi/asm-generic/mman-common.h
+++ b/include/uapi/asm-generic/mman-common.h
@@ -71,7 +71,5 @@
#define PKEY_DISABLE_ACCESS 0x1
#define PKEY_DISABLE_WRITE 0x2
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
- PKEY_DISABLE_WRITE)
-
+#define PKEY_DISABLE_READ 0x10
#endif /* __ASM_GENERIC_MMAN_COMMON_H */
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 6d33162..f4cefc3 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -597,7 +597,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
if (flags)
return -EINVAL;
/* check for unsupported init values */
- if (init_val & ~PKEY_ACCESS_MASK)
+ if (!arch_pkey_access_rights_valid(init_val))
return -EINVAL;
down_write(¤t->mm->mmap_sem);
-----------------------------------------------------------------------------
WARNING: multiple messages have this Message-ID (diff)
From: Ram Pai <linuxram@us.ibm.com>
To: Florian Weimer <fweimer@redhat.com>
Cc: Dave Hansen <dave.hansen@intel.com>,
linux-mm@kvack.org, linux-api@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org
Subject: Re: pkeys: Reserve PKEY_DISABLE_READ
Date: Mon, 3 Dec 2018 22:23:18 -0800 [thread overview]
Message-ID: <20181204062318.GC11930@ram.oc3035372033.ibm.com> (raw)
In-Reply-To: <87pnuibobh.fsf@oldenburg.str.redhat.com>
On Mon, Dec 03, 2018 at 04:52:02PM +0100, Florian Weimer wrote:
> * Ram Pai:
>
> > So the problem is as follows:
> >
> > Currently the kernel supports 'disable-write' and 'disable-access'.
> >
> > On x86, cpu supports 'disable-write' and 'disable-access'. This
> > matches with what the kernel supports. All good.
> >
> > However on power, cpu supports 'disable-read' too. Since userspace can
> > program the cpu directly, userspace has the ability to set
> > 'disable-read' too. This can lead to inconsistency between the kernel
> > and the userspace.
> >
> > We want the kernel to match userspace on all architectures.
>
> Correct.
>
> > Proposed Solution:
> >
> > Enhance the kernel to understand 'disable-read', and facilitate architectures
> > that understand 'disable-read' to allow it.
> >
> > Also explicitly define the semantics of disable-access as
> > 'disable-read and disable-write'
> >
> > Did I get this right? Assuming I did, the implementation has to do
> > the following --
> >
> > On power, sys_pkey_alloc() should succeed if the init_val
> > is PKEY_DISABLE_READ, PKEY_DISABLE_WRITE, PKEY_DISABLE_ACCESS
> > or any combination of the three.
>
> Agreed.
>
> > On x86, sys_pkey_alloc() should succeed if the init_val is
> > PKEY_DISABLE_WRITE or PKEY_DISABLE_ACCESS or PKEY_DISABLE_READ
> > or any combination of the three, except PKEY_DISABLE_READ
> > specified all by itself.
>
> Again agreed. That's a clever way of phrasing it actually.
>
> > On all other arches, none of the flags are supported.
> >
> >
> > Are we on the same plate?
>
> I think so, thanks.
>
> Florian
Ok. here is a patch, compiled but not tested. See if this meets the
specifications.
-----------------------------------------------------------------------------------
commit 3dc06e73f3795921265d5d1d935e428deab01616
Author: Ram Pai <linuxram@us.ibm.com>
Date: Tue Dec 4 00:04:11 2018 -0500
pkeys: add support of PKEY_DISABLE_READ
Kernel supports 'disable-write' and 'disable-access'.
x86 cpu supports 'disable-write' and 'disable-access'. This
matches with the kernel support.
However POWER cpu supports 'disable-read' too. Since userspace can
program the cpu directly, userspace has the ability to set
'disable-read' too. This can lead to inconsistency between the kernel
and the userspace.
Make kernel match userspace on all architectures.
Enhance the kernel to understand 'disable-read', and facilitate architectures
that understand 'disable-read' to allow it.
Define the semantics of disable-access as 'disable-read and disable-write'
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h
index 20ebf15..4bd09d0 100644
--- a/arch/powerpc/include/asm/pkeys.h
+++ b/arch/powerpc/include/asm/pkeys.h
@@ -19,11 +19,7 @@
#define ARCH_VM_PKEY_FLAGS (VM_PKEY_BIT0 | VM_PKEY_BIT1 | VM_PKEY_BIT2 | \
VM_PKEY_BIT3 | VM_PKEY_BIT4)
-/* Override any generic PKEY permission defines */
#define PKEY_DISABLE_EXECUTE 0x4
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS | \
- PKEY_DISABLE_WRITE | \
- PKEY_DISABLE_EXECUTE)
static inline u64 pkey_to_vmflag_bits(u16 pkey)
{
@@ -199,6 +195,16 @@ static inline bool arch_pkeys_enabled(void)
return !static_branch_likely(&pkey_disabled);
}
+extern bool __arch_pkey_access_rights_valid(unsigned long rights);
+
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ if (static_branch_likely(&pkey_disabled))
+ return false;
+
+ return __arch_pkey_access_rights_valid(rights);
+}
+
extern void pkey_mm_init(struct mm_struct *mm);
extern bool arch_supports_pkeys(int cap);
extern unsigned int arch_usable_pkeys(void);
diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h
index 65065ce..e63bc37 100644
--- a/arch/powerpc/include/uapi/asm/mman.h
+++ b/arch/powerpc/include/uapi/asm/mman.h
@@ -30,10 +30,4 @@
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
-/* Override any generic PKEY permission defines */
-#define PKEY_DISABLE_EXECUTE 0x4
-#undef PKEY_ACCESS_MASK
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
- PKEY_DISABLE_WRITE |\
- PKEY_DISABLE_EXECUTE)
#endif /* _UAPI_ASM_POWERPC_MMAN_H */
diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
index 5d65c47..a4f288b 100644
--- a/arch/powerpc/mm/pkeys.c
+++ b/arch/powerpc/mm/pkeys.c
@@ -67,7 +67,7 @@ int pkey_initialize(void)
* Ensure that the bits a distinct.
*/
BUILD_BUG_ON(PKEY_DISABLE_EXECUTE &
- (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
+ (PKEY_DISABLE_READ | PKEY_DISABLE_WRITE | PKEY_DISABLE_ACCESS));
/*
* pkey_to_vmflag_bits() assumes that the pkey bits are contiguous
@@ -259,11 +259,20 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
new_amr_bits |= AMR_RD_BIT | AMR_WR_BIT;
else if (init_val & PKEY_DISABLE_WRITE)
new_amr_bits |= AMR_WR_BIT;
+ else if (init_val & PKEY_DISABLE_READ)
+ new_amr_bits |= AMR_RD_BIT;
init_amr(pkey, new_amr_bits);
return 0;
}
+bool __arch_pkey_access_rights_valid(unsigned long rights)
+{
+ unsigned long mask = PKEY_DISABLE_READ | PKEY_DISABLE_WRITE |\
+ PKEY_DISABLE_ACCESS;
+ return (rights & mask);
+}
+
void thread_pkey_regs_save(struct thread_struct *thread)
{
if (static_branch_likely(&pkey_disabled))
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index 19b137f..4f36a7e 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -14,6 +14,15 @@ static inline bool arch_pkeys_enabled(void)
return boot_cpu_has(X86_FEATURE_OSPKE);
}
+extern bool __arch_pkey_access_rights_valid(unsigned long rights);
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ if (!boot_cpu_has(X86_FEATURE_OSPKE))
+ return false;
+
+ return __arch_pkey_access_rights_valid(rights);
+}
+
/*
* Try to dedicate one of the protection keys to be used as an
* execute-only protection key.
diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c
index 6e98e0a..fcfe1b2 100644
--- a/arch/x86/mm/pkeys.c
+++ b/arch/x86/mm/pkeys.c
@@ -72,6 +72,17 @@ int __execute_only_pkey(struct mm_struct *mm)
return execute_only_pkey;
}
+bool __arch_pkey_access_rights_valid(unsigned long rights)
+{
+ unsigned long mask = PKEY_DISABLE_READ | PKEY_DISABLE_WRITE |\
+ PKEY_DISABLE_ACCESS;
+ if (!(rights & mask))
+ return false;
+
+ /* return failure if only PKEY_DISABLE_READ is specified */
+ return ((rights & mask) != PKEY_DISABLE_READ);
+}
+
static inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma)
{
/* Do this check first since the vm_flags should be hot */
diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
index 2955ba97..2c330fa 100644
--- a/include/linux/pkeys.h
+++ b/include/linux/pkeys.h
@@ -48,6 +48,11 @@ static inline void copy_init_pkru_to_fpregs(void)
{
}
+static inline bool arch_pkey_access_rights_valid(unsigned long rights)
+{
+ return false;
+}
+
#endif /* ! CONFIG_ARCH_HAS_PKEYS */
#endif /* _LINUX_PKEYS_H */
diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h
index e7ee328..d2e1a5e 100644
--- a/include/uapi/asm-generic/mman-common.h
+++ b/include/uapi/asm-generic/mman-common.h
@@ -71,7 +71,5 @@
#define PKEY_DISABLE_ACCESS 0x1
#define PKEY_DISABLE_WRITE 0x2
-#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
- PKEY_DISABLE_WRITE)
-
+#define PKEY_DISABLE_READ 0x10
#endif /* __ASM_GENERIC_MMAN_COMMON_H */
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 6d33162..f4cefc3 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -597,7 +597,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
if (flags)
return -EINVAL;
/* check for unsupported init values */
- if (init_val & ~PKEY_ACCESS_MASK)
+ if (!arch_pkey_access_rights_valid(init_val))
return -EINVAL;
down_write(¤t->mm->mmap_sem);
-----------------------------------------------------------------------------
next prev parent reply other threads:[~2018-12-04 6:23 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-11-08 12:05 pkeys: Reserve PKEY_DISABLE_READ Florian Weimer
2018-11-08 14:57 ` Dave Hansen
2018-11-08 15:01 ` Florian Weimer
2018-11-08 17:14 ` Dave Hansen
2018-11-08 17:37 ` Florian Weimer
2018-11-08 20:12 ` Ram Pai
2018-11-08 20:12 ` Ram Pai
2018-11-08 20:23 ` Florian Weimer
2018-11-08 20:23 ` Florian Weimer
2018-11-09 18:09 ` Ram Pai
2018-11-09 18:09 ` Ram Pai
2018-11-12 12:00 ` Florian Weimer
2018-11-12 12:00 ` Florian Weimer
2018-11-27 10:23 ` Ram Pai
2018-11-27 10:23 ` Ram Pai
2018-11-27 11:57 ` Florian Weimer
2018-11-27 11:57 ` Florian Weimer
2018-11-27 15:31 ` Dave Hansen
2018-11-27 15:31 ` Dave Hansen
2018-11-29 11:37 ` Florian Weimer
2018-11-29 11:37 ` Florian Weimer
2018-12-03 4:02 ` Ram Pai
2018-12-03 4:02 ` Ram Pai
2018-12-03 15:52 ` Florian Weimer
2018-12-03 15:52 ` Florian Weimer
2018-12-04 6:23 ` Ram Pai [this message]
2018-12-04 6:23 ` Ram Pai
2018-12-05 13:00 ` Florian Weimer
2018-12-05 13:00 ` Florian Weimer
2018-12-05 20:23 ` Ram Pai
2018-12-05 20:23 ` Ram Pai
2018-12-05 16:21 ` Andy Lutomirski
2018-12-05 16:21 ` Andy Lutomirski
2018-12-05 20:36 ` Ram Pai
2018-12-05 20:36 ` Ram Pai
2018-11-08 20:08 ` Ram Pai
2018-11-08 20:11 ` Dave Hansen
2018-11-08 20:14 ` Florian Weimer
2018-11-08 19:22 ` Ram Pai
2018-11-08 19:22 ` Ram Pai
2018-11-12 10:29 ` Florian Weimer
2018-11-12 10:29 ` Florian Weimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181204062318.GC11930@ram.oc3035372033.ibm.com \
--to=linuxram@us.ibm.com \
--cc=dave.hansen@intel.com \
--cc=fweimer@redhat.com \
--cc=linux-api@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linuxppc-dev@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.