diff mbox

x86: Remove ARCH_CET_LEGACY_BITMAP [BZ #25397]

Message ID 20200307002530.54013-1-hjl.tools@gmail.com
State New
Headers show

Commit Message

H.J. Lu March 7, 2020, 12:25 a.m. UTC
Since legacy bitmap covers legacy JIT engine, but doesn't cover jitted
code generated by legacy JIT engine, it isn't very useful.  This patch
treats indirect branch tracking (IBT) similar to shadow stack (SHSTK) by
removing legacy bitmap support so that both IBT and SHSTK are disabled
on legacy JIT engine.
---
 sysdeps/unix/sysv/linux/x86/dl-cet.h          |  20 --
 .../unix/sysv/linux/x86/include/asm/prctl.h   |   5 -
 sysdeps/x86/Makefile                          |   7 +-
 sysdeps/x86/dl-cet.c                          | 197 ++++--------------
 sysdeps/x86/dl-procruntime.c                  |  11 -
 sysdeps/x86/tst-cet-legacy-4.c                |  19 +-
 sysdeps/x86/tst-cet-legacy-7.c                |  40 ++++
 7 files changed, 92 insertions(+), 207 deletions(-)
 create mode 100644 sysdeps/x86/tst-cet-legacy-7.c

Comments

Florian Weimer March 7, 2020, 12:06 p.m. UTC | #1
* H. J. Lu:

> +  funcp = mmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
> +		MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);

An alternative approach would automatically allocate a legacy bitmap
for RWX mappings under the right circumstances (maybe even looking and
the caller address in mmap).  Have you considered that?

I'm not question the removal of the legacy bitmap as such.  It looks
like the right call.  I just want to collect some thoughts for
posterity.
H.J. Lu March 7, 2020, 1:25 p.m. UTC | #2
On Sat, Mar 7, 2020 at 4:08 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>
> * H. J. Lu:
>
> > +  funcp = mmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
> > +             MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
>
> An alternative approach would automatically allocate a legacy bitmap
> for RWX mappings under the right circumstances (maybe even looking and

Also mprotect.

> the caller address in mmap).  Have you considered that?

It can become very complicated since mmap and mprotect can be called
indirectly.   It is hard to tell if the real user of such memory is legacy code
or not.

> I'm not question the removal of the legacy bitmap as such.  It looks
> like the right call.  I just want to collect some thoughts for
> posterity.

I think we should leave out legacy bitmap in the first phase.   We can
always put it back later.

BTW, now I can boot the updated Fedora 31 in GUI on CET processor with
CET enabled kernel.
Florian Weimer March 7, 2020, 1:36 p.m. UTC | #3
* H. J. Lu:

> On Sat, Mar 7, 2020 at 4:08 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>>
>> * H. J. Lu:
>>
>> > +  funcp = mmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
>> > +             MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
>>
>> An alternative approach would automatically allocate a legacy bitmap
>> for RWX mappings under the right circumstances (maybe even looking and
>
> Also mprotect.

Oh, I was wondering if we should piggy-back on the old (non-upstream)
execstack logic, where later mprotect was problematic if the mapping
was created RWX.

>> the caller address in mmap).  Have you considered that?
>
> It can become very complicated since mmap and mprotect can be called
> indirectly.  It is hard to tell if the real user of such memory is
> legacy code or not.

That's true.

>> I'm not question the removal of the legacy bitmap as such.  It looks
>> like the right call.  I just want to collect some thoughts for
>> posterity.
>
> I think we should leave out legacy bitmap in the first phase.   We can
> always put it back later.

Fair point.  Let me review your patch then.

> BTW, now I can boot the updated Fedora 31 in GUI on CET processor with
> CET enabled kernel.

Wow, excellent news!
Florian Weimer March 9, 2020, 10:03 a.m. UTC | #4
* H. J. Lu:

> diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c
> index ca3b5849bc..2be7a3a49f 100644
> --- a/sysdeps/x86/dl-cet.c
> +++ b/sysdeps/x86/dl-cet.c

> +	      /* IBT is enabled only if it is enabled in executable as
> +		 well as all shared objects.  */
> +	      enable_ibt &= (enable_ibt_type == CET_ALWAYS_ON
> +			     || (l->l_cet & lc_ibt) != 0);

As a general comment, I think we should add more logging as to way
processes lose protection.  But that does not need to happen with this
patch.

> +		  /* When IBT is enabled, we can't dlopening a shared
> +		     object without IBT.  */

“we cannot dlopen”

> +		  if (enable_ibt != ibt_enabled)
> +		    _dl_signal_error (EINVAL, l->l_name, "dlopen",
> +				      N_("indirect branch tracking isn't enabled"));

I think the error message should say *where* indirect branch tracking
is not enabled.  I assume this is a property of the loaded object.


> +		  /* When SHSTK is enabled, we can't dlopening a shared
> +		     object without SHSTK.  */
> +		  if (enable_shstk != shstk_enabled)
> +		    _dl_signal_error (EINVAL, l->l_name, "dlopen",
> +				      N_("shadow stack isn't enabled"));

See above.

> diff --git a/sysdeps/x86/tst-cet-legacy-7.c b/sysdeps/x86/tst-cet-legacy-7.c
> new file mode 100644
> index 0000000000..e6f5d6f145
> --- /dev/null
> +++ b/sysdeps/x86/tst-cet-legacy-7.c

> +  funcp = mmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
> +		MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> +  if (funcp == MAP_FAILED)
> +    FAIL_EXIT1 ("mmap failed (errno=%d)", errno);

You could use xmmap here.

Rest of the patch looks okay to me.
diff mbox

Patch

diff --git a/sysdeps/unix/sysv/linux/x86/dl-cet.h b/sysdeps/unix/sysv/linux/x86/dl-cet.h
index 9b2aaa238c..ae97a433a2 100644
--- a/sysdeps/unix/sysv/linux/x86/dl-cet.h
+++ b/sysdeps/unix/sysv/linux/x86/dl-cet.h
@@ -18,26 +18,6 @@ 
 #include <sys/prctl.h>
 #include <asm/prctl.h>
 
-static inline int __attribute__ ((always_inline))
-dl_cet_allocate_legacy_bitmap (unsigned long *legacy_bitmap)
-{
-  /* Allocate legacy bitmap.  */
-#ifdef __LP64__
-  return (int) INTERNAL_SYSCALL_CALL (arch_prctl,
-				      ARCH_CET_LEGACY_BITMAP, legacy_bitmap);
-#else
-  unsigned long long legacy_bitmap_u64[2];
-  int res = INTERNAL_SYSCALL_CALL (arch_prctl,
-				   ARCH_CET_LEGACY_BITMAP, legacy_bitmap_u64);
-  if (res == 0)
-    {
-      legacy_bitmap[0] = legacy_bitmap_u64[0];
-      legacy_bitmap[1] = legacy_bitmap_u64[1];
-    }
-  return res;
-#endif
-}
-
 static inline int __attribute__ ((always_inline))
 dl_cet_disable_cet (unsigned int cet_feature)
 {
diff --git a/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h b/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h
index f67f3299b9..45ad0b052f 100644
--- a/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h
+++ b/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h
@@ -24,9 +24,4 @@ 
      OUT: allocated shadow stack address: *addr.
  */
 # define ARCH_CET_ALLOC_SHSTK	0x3004
-/* Return legacy region bitmap info in unsigned long long *addr:
-     address: addr[0].
-     size: addr[1].
- */
-# define ARCH_CET_LEGACY_BITMAP	0x3005
 #endif /* ARCH_CET_STATUS */
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 95182a508c..ec96b59a78 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -20,7 +20,7 @@  sysdep-dl-routines += dl-cet
 
 tests += tst-cet-legacy-1 tst-cet-legacy-1a tst-cet-legacy-2 \
 	 tst-cet-legacy-2a tst-cet-legacy-3 tst-cet-legacy-4 \
-	 tst-cet-legacy-5a tst-cet-legacy-6a
+	 tst-cet-legacy-5a tst-cet-legacy-6a tst-cet-legacy-7
 tst-cet-legacy-1a-ARGS = -- $(host-test-program-cmd)
 ifneq (no,$(have-tunables))
 tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \
@@ -43,14 +43,15 @@  CFLAGS-tst-cet-legacy-4b.c += -fcf-protection
 CFLAGS-tst-cet-legacy-mod-4.c += -fcf-protection=none
 CFLAGS-tst-cet-legacy-5a.c += -fcf-protection
 CFLAGS-tst-cet-legacy-5b.c += -fcf-protection
-CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=branch
 CFLAGS-tst-cet-legacy-mod-5b.c += -fcf-protection
 CFLAGS-tst-cet-legacy-mod-5c.c += -fcf-protection
 CFLAGS-tst-cet-legacy-6a.c += -fcf-protection
 CFLAGS-tst-cet-legacy-6b.c += -fcf-protection
-CFLAGS-tst-cet-legacy-mod-6a.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-mod-6a.c += -fcf-protection=branch
 CFLAGS-tst-cet-legacy-mod-6b.c += -fcf-protection
 CFLAGS-tst-cet-legacy-mod-6c.c += -fcf-protection
+CFLAGS-tst-cet-legacy-7.c += -fcf-protection=none
 
 $(objpfx)tst-cet-legacy-1: $(objpfx)tst-cet-legacy-mod-1.so \
 		       $(objpfx)tst-cet-legacy-mod-2.so
diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c
index ca3b5849bc..2be7a3a49f 100644
--- a/sysdeps/x86/dl-cet.c
+++ b/sysdeps/x86/dl-cet.c
@@ -33,63 +33,6 @@ 
 # error GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
 #endif
 
-static int
-dl_cet_mark_legacy_region (struct link_map *l)
-{
-  /* Mark PT_LOAD segments with PF_X in legacy code page bitmap.  */
-  size_t i, phnum = l->l_phnum;
-  const ElfW(Phdr) *phdr = l->l_phdr;
-#ifdef __x86_64__
-  typedef unsigned long long word_t;
-#else
-  typedef unsigned long word_t;
-#endif
-  unsigned int bits_to_set;
-  word_t mask_to_set;
-#define BITS_PER_WORD (sizeof (word_t) * 8)
-#define BITMAP_FIRST_WORD_MASK(start) \
-  (~((word_t) 0) << ((start) & (BITS_PER_WORD - 1)))
-#define BITMAP_LAST_WORD_MASK(nbits) \
-  (~((word_t) 0) >> (-(nbits) & (BITS_PER_WORD - 1)))
-
-  word_t *bitmap = (word_t *) GL(dl_x86_legacy_bitmap)[0];
-  word_t bitmap_size = GL(dl_x86_legacy_bitmap)[1];
-  word_t *p;
-  size_t page_size = GLRO(dl_pagesize);
-
-  for (i = 0; i < phnum; i++)
-    if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X))
-      {
-	/* One bit in legacy bitmap represents a page.  */
-	ElfW(Addr) start = (phdr[i].p_vaddr + l->l_addr) / page_size;
-	ElfW(Addr) len = (phdr[i].p_memsz + page_size - 1) / page_size;
-	ElfW(Addr) end = start + len;
-
-	if ((end / 8) > bitmap_size)
-	  return -EINVAL;
-
-	p = bitmap + (start / BITS_PER_WORD);
-	bits_to_set = BITS_PER_WORD - (start % BITS_PER_WORD);
-	mask_to_set = BITMAP_FIRST_WORD_MASK (start);
-
-	while (len >= bits_to_set)
-	  {
-	    *p |= mask_to_set;
-	    len -= bits_to_set;
-	    bits_to_set = BITS_PER_WORD;
-	    mask_to_set = ~((word_t) 0);
-	    p++;
-	  }
-	if (len)
-	  {
-	    mask_to_set &= BITMAP_LAST_WORD_MASK (end);
-	    *p |= mask_to_set;
-	  }
-      }
-
-  return 0;
-}
-
 /* Check if object M is compatible with CET.  */
 
 static void
@@ -142,10 +85,7 @@  dl_cet_check (struct link_map *m, const char *program)
 	 support IBT nor SHSTK.  */
       if (enable_ibt || enable_shstk)
 	{
-	  int res;
 	  unsigned int i;
-	  unsigned int first_legacy, last_legacy;
-	  bool need_legacy_bitmap = false;
 
 	  i = m->l_searchlist.r_nlist;
 	  while (i-- > 0)
@@ -167,115 +107,52 @@  dl_cet_check (struct link_map *m, const char *program)
 		continue;
 #endif
 
-	      if (enable_ibt
-		  && enable_ibt_type != CET_ALWAYS_ON
-		  && !(l->l_cet & lc_ibt))
-		{
-		  /* Remember the first and last legacy objects.  */
-		  if (!need_legacy_bitmap)
-		    last_legacy = i;
-		  first_legacy = i;
-		  need_legacy_bitmap = true;
-		}
+	      /* IBT is enabled only if it is enabled in executable as
+		 well as all shared objects.  */
+	      enable_ibt &= (enable_ibt_type == CET_ALWAYS_ON
+			     || (l->l_cet & lc_ibt) != 0);
 
 	      /* SHSTK is enabled only if it is enabled in executable as
 		 well as all shared objects.  */
 	      enable_shstk &= (enable_shstk_type == CET_ALWAYS_ON
 			       || (l->l_cet & lc_shstk) != 0);
 	    }
+	}
 
-	  if (need_legacy_bitmap)
+      bool cet_feature_changed = false;
+
+      if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled)
+	{
+	  if (!program)
 	    {
-	      if (GL(dl_x86_legacy_bitmap)[0])
+	      if (enable_ibt_type != CET_PERMISSIVE)
 		{
-		  /* Change legacy bitmap to writable.  */
-		  if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0],
-				  GL(dl_x86_legacy_bitmap)[1],
-				  PROT_READ | PROT_WRITE) < 0)
-		    {
-mprotect_failure:
-		      if (program)
-			_dl_fatal_printf ("%s: mprotect legacy bitmap failed\n",
-					  l->l_name);
-		      else
-			_dl_signal_error (EINVAL, l->l_name, "dlopen",
-					  N_("mprotect legacy bitmap failed"));
-		    }
-		}
-	      else
-		{
-		  /* Allocate legacy bitmap.  */
-		  int res = dl_cet_allocate_legacy_bitmap
-		    (GL(dl_x86_legacy_bitmap));
-		  if (res != 0)
-		    {
-		      if (program)
-			_dl_fatal_printf ("%s: legacy bitmap isn't available\n",
-					  l->l_name);
-		      else
-			_dl_signal_error (EINVAL, l->l_name, "dlopen",
-					  N_("legacy bitmap isn't available"));
-		    }
+		  /* When IBT is enabled, we can't dlopening a shared
+		     object without IBT.  */
+		  if (enable_ibt != ibt_enabled)
+		    _dl_signal_error (EINVAL, l->l_name, "dlopen",
+				      N_("indirect branch tracking isn't enabled"));
 		}
 
-	      /* Put legacy shared objects in legacy bitmap.  */
-	      for (i = first_legacy; i <= last_legacy; i++)
+	      if (enable_shstk_type != CET_PERMISSIVE)
 		{
-		  l = m->l_initfini[i];
-
-		  if (l->l_init_called || (l->l_cet & lc_ibt))
-		    continue;
-
-#ifdef SHARED
-		  if (l == &GL(dl_rtld_map)
-		      ||  l->l_real == &GL(dl_rtld_map)
-		      || (program && l == m))
-		    continue;
-#endif
-
-		  /* If IBT is enabled in executable and IBT isn't enabled
-		     in this shard object, mark PT_LOAD segments with PF_X
-		     in legacy code page bitmap.  */
-		  res = dl_cet_mark_legacy_region (l);
-		  if (res != 0)
-		    {
-		      if (program)
-			_dl_fatal_printf ("%s: failed to mark legacy code region\n",
-					  l->l_name);
-		      else
-			_dl_signal_error (-res, l->l_name, "dlopen",
-					  N_("failed to mark legacy code region"));
-		    }
+		  /* When SHSTK is enabled, we can't dlopening a shared
+		     object without SHSTK.  */
+		  if (enable_shstk != shstk_enabled)
+		    _dl_signal_error (EINVAL, l->l_name, "dlopen",
+				      N_("shadow stack isn't enabled"));
 		}
 
-	      /* Change legacy bitmap to read-only.  */
-	      if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0],
-			      GL(dl_x86_legacy_bitmap)[1], PROT_READ) < 0)
-		goto mprotect_failure;
-	    }
-	}
-
-      bool cet_feature_changed = false;
-
-      if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled)
-	{
-	  if (!program
-	      && enable_shstk_type != CET_PERMISSIVE)
-	    {
-	      /* When SHSTK is enabled, we can't dlopening a shared
-		 object without SHSTK.  */
-	      if (enable_shstk != shstk_enabled)
-		_dl_signal_error (EINVAL, l->l_name, "dlopen",
-				  N_("shadow stack isn't enabled"));
-	      return;
+	      if (enable_ibt_type != CET_PERMISSIVE
+		  && enable_shstk_type != CET_PERMISSIVE)
+		return;
 	    }
 
 	  /* Disable IBT and/or SHSTK if they are enabled by kernel, but
 	     disabled in executable or shared objects.  */
 	  unsigned int cet_feature = 0;
 
-	  /* Disable IBT only during program startup.  */
-	  if (program && !enable_ibt)
+	  if (!enable_ibt)
 	    cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT;
 	  if (!enable_shstk)
 	    cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
@@ -297,17 +174,21 @@  mprotect_failure:
 	}
 
 #ifdef SHARED
-      if (program
-	  && (!shstk_enabled
-	      || enable_shstk_type != CET_PERMISSIVE)
-	  && (ibt_enabled || shstk_enabled))
+      if (program && (ibt_enabled || shstk_enabled))
 	{
-	  /* Lock CET if IBT or SHSTK is enabled in executable.  Don't
-	     lock CET if SHSTK is enabled permissively.  */
-	  int res = dl_cet_lock_cet ();
-	  if (res != 0)
-	    _dl_fatal_printf ("%s: can't lock CET\n", program);
+	  if ((!ibt_enabled
+	       || enable_ibt_type != CET_PERMISSIVE)
+	      && (!shstk_enabled
+		  || enable_shstk_type != CET_PERMISSIVE))
+	    {
+	      /* Lock CET if IBT or SHSTK is enabled in executable unless
+	         IBT or SHSTK is enabled permissively.  */
+	      int res = dl_cet_lock_cet ();
+	      if (res != 0)
+		_dl_fatal_printf ("%s: can't lock CET\n", program);
+	    }
 
+	  /* Set feature_1 if IBT or SHSTK is enabled in executable.  */
 	  cet_feature_changed = true;
 	}
 #endif
diff --git a/sysdeps/x86/dl-procruntime.c b/sysdeps/x86/dl-procruntime.c
index fb36801f3e..5e39a38133 100644
--- a/sysdeps/x86/dl-procruntime.c
+++ b/sysdeps/x86/dl-procruntime.c
@@ -54,15 +54,4 @@  PROCINFO_CLASS unsigned int _dl_x86_feature_1[2]
 # else
 ,
 # endif
-
-# if !defined PROCINFO_DECL && defined SHARED
-  ._dl_x86_legacy_bitmap
-# else
-PROCINFO_CLASS unsigned long _dl_x86_legacy_bitmap[2]
-# endif
-# if !defined SHARED || defined PROCINFO_DECL
-;
-# else
-,
-# endif
 #endif
diff --git a/sysdeps/x86/tst-cet-legacy-4.c b/sysdeps/x86/tst-cet-legacy-4.c
index a77078afc9..da1f3d1e0d 100644
--- a/sysdeps/x86/tst-cet-legacy-4.c
+++ b/sysdeps/x86/tst-cet-legacy-4.c
@@ -20,6 +20,9 @@ 
 #include <dlfcn.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+
+#include <support/check.h>
 
 static int
 do_test (void)
@@ -31,22 +34,18 @@  do_test (void)
   h = dlopen (modname, RTLD_LAZY);
   if (h == NULL)
     {
-      printf ("cannot open '%s': %s\n", modname, dlerror ());
-      exit (1);
+      const char *err = dlerror ();
+      if (!strstr (err, "indirect branch tracking isn't enabled"))
+	FAIL_EXIT1 ("incorrect dlopen '%s' error: %s\n", modname, err);
+      return 0;
     }
 
   fp = dlsym (h, "test");
   if (fp == NULL)
-    {
-      printf ("cannot get symbol 'test': %s\n", dlerror ());
-      exit (1);
-    }
+    FAIL_EXIT1 ("cannot get symbol 'test': %s\n", dlerror ());
 
   if (fp () != 0)
-    {
-      puts ("test () != 0");
-      exit (1);
-    }
+    FAIL_EXIT1 ("test () != 0");
 
   dlclose (h);
 
diff --git a/sysdeps/x86/tst-cet-legacy-7.c b/sysdeps/x86/tst-cet-legacy-7.c
new file mode 100644
index 0000000000..e6f5d6f145
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-7.c
@@ -0,0 +1,40 @@ 
+/* Check compatibility of legacy executable with a JIT engine.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  void (*funcp) (void);
+  funcp = mmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
+		MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (funcp == MAP_FAILED)
+    FAIL_EXIT1 ("mmap failed (errno=%d)", errno);
+  printf ("mmap = %p\n", funcp);
+  /* Write RET instruction.  */
+  *(char *) funcp = 0xc3;
+  funcp ();
+  return 0;
+}
+
+#include <support/test-driver.c>