RCS log gets up to large numbers of revisions then RCS will core dump

haoming chu <[email protected]> Wed, 26 Oct 2016 15:07:57 +0800
Newsgroups gmane.comp.version-control.rcs.bugs
Organization Oracle Corporation
Message-ID <[email protected]>
RCS has a known bug which when an RCS log gets up to large numbers of 
revisions then RCS will core dump. We have seen this when the RCS log 
gets upwards of 80-90,000 entries. RCS will continue to core dump on any 
command(co,ci) until the log file is truncated, which is the only 
workaround for this issue. The only time we have seen this is in an 
error state from a program which is updating a file consistently over 
time due to a bug.

for example:

In rcs-5.7-37.el6.x86_64.rpm in CentOS 6.5

# ci -l ./ifcfg-bond3
./RCS/ifcfg-bond3,v <-- ./ifcfg-bond3
Segmentation fault (core dumped)

We found that root cause is:

Infinite recursion without cut-off conditions was used in function 
'puttree' in 'rcsgen.c'(line: 553).

So stack collapsed when too many entries in rcs log file.
(ifcfg-bond3,v in attach.tar.gz).

void
puttree(root, fout)
struct hshentry const *root;
register FILE *fout;
/* Output the delta tree with base ROOT in preorder to FOUT. */
{
struct branchhead const *nextbranch;

if (!root) return;

if (root->selector)
putdelta(root, fout);

puttree(root->next, fout);

nextbranch = root->branches;
while (nextbranch) {
puttree(nextbranch->hsh, fout);
nextbranch = nextbranch->nextbranch;
}
}

We make a patch (rcs-5.7-37.patch) to workround this problem in
rcs-5.7-37.el6.x86_64.

(Replace recursion with iteration of the loop.)
rcs-5.7-37.patch (text/plain, 6.2 KB)
diff -ruNa /root/rcs-5.7/src/rcsbase.h ./rcs-5.7/src/rcsbase.h
--- /root/rcs-5.7/src/rcsbase.h	1995-06-16 02:19:24.000000000 -0400
+++ ./rcs-5.7/src/rcsbase.h	2015-12-28 00:06:08.290116532 -0500
@@ -188,7 +188,6 @@
 
 #include "conf.h"
 
-
 #define EXIT_TROUBLE DIFF_TROUBLE
 
 #ifdef _POSIX_PATH_MAX
@@ -234,6 +233,7 @@
 #define true     1
 #define false    0
 
+#define STACK_INC_SIZE  8589934592  /* 8192 * 1024 * 1024 */
 
 /*
  * RILE - readonly file
@@ -359,10 +359,10 @@
 	char const	  * state;    /* state of revision (Exp by default) */
 	char const	  * name;     /* name (if any) by which retrieved   */
 	struct cbuf	    log;      /* log message requested at checkin   */
-        struct branchhead * branches; /* list of first revisions on branches*/
+    struct branchhead * branches; /* list of first revisions on branches*/
 	struct cbuf	    ig;	      /* ignored phrases in admin part	    */
 	struct cbuf	    igtext;   /* ignored phrases in deltatext part  */
-        struct hshentry   * next;     /* next revision on same branch       */
+    struct hshentry   * next;     /* next revision on same branch       */
 	struct hshentry   * nexthsh;  /* next revision with same hash value */
 	long		    insertlns;/* lines inserted (computed by rlog)  */
 	long		    deletelns;/* lines deleted  (computed by rlog)  */
@@ -371,8 +371,8 @@
 
 /* list of hash entries */
 struct hshentries {
-	struct hshentries *rest;
-	struct hshentry *first;
+	struct hshentries * rest;
+	struct hshentry   * first;
 };
 
 /* list element for branch lists */
@@ -383,14 +383,14 @@
 
 /* accesslist element */
 struct access {
-	char const	  * login;
-        struct access     * nextaccess;
+	char const	      * login;
+    struct access     * nextaccess;
 };
 
 /* list element for locks  */
 struct rcslock {
-	char const	  * login;
-        struct hshentry   * delta;
+	char const	      * login;
+    struct hshentry   * delta;
 	struct rcslock    * nextlock;
 };
 
@@ -401,6 +401,13 @@
         struct assoc      * nextassoc;
 };
 
+/* point stack */
+struct stack
+{
+  void    **base;
+  void    **top;
+  size_t  stacksize;
+};
 
 #define mainArgs (argc,argv) int argc; char **argv;
 
@@ -422,7 +429,7 @@
 #define IDH             "Id"
 #define LOCKER          "Locker"
 #define LOG             "Log"
-#define NAME		"Name"
+#define NAME		    "Name"
 #define RCSFILE         "RCSfile"
 #define REVISION        "Revision"
 #define SOURCE          "Source"
diff -ruNa /root/rcs-5.7/src/rcsgen.c ./rcs-5.7/src/rcsgen.c
--- /root/rcs-5.7/src/rcsgen.c	1995-06-16 02:19:24.000000000 -0400
+++ ./rcs-5.7/src/rcsgen.c	2015-12-28 00:06:06.512116495 -0500
@@ -136,9 +136,6 @@
  * corrected type of variables assigned to by getc (char --> int)
  */
 
-
-
-
 #include "rcsbase.h"
 
 libId(genId, "$Id: rcsgen.c,v 5.16 1995/06/16 06:19:24 eggert Exp $")
@@ -526,7 +523,7 @@
 	static void
 putdelta(node, fout)
 	register struct hshentry const *node;
-	register FILE * fout;
+	register FILE *fout;
 /* Output the delta NODE to FOUT.  */
 {
 	struct branchhead const *nextbranch;
@@ -549,6 +546,98 @@
 	awrite(node->ig.string, node->ig.size, fout);
 }
 
+	static void
+init(pstack)
+	register struct stack *pstack;
+/* Init stack.  */
+{
+    pstack->stacksize = (size_t)(STACK_INC_SIZE);
+    if ((pstack->base = malloc(pstack->stacksize)) == NULL)
+    {
+      error("Stack init error!");
+      exit(-1);
+    }
+    pstack->top  = pstack->base;
+}
+
+	static void
+destroy(pstack)
+	register struct stack *pstack;
+/* Destroy stack.  */
+{
+    if (pstack->base)
+      free(pstack->base);
+    pstack->base      = NULL;
+    pstack->top       = NULL;
+    pstack->stacksize = 0;
+}
+
+	static int
+push(pstack, element)
+	register struct stack *pstack;
+	register void         **element;
+/* Push element to top of stack.  */
+{
+  void **temp = NULL;
+  
+  if (pstack->top - pstack->base >= pstack->stacksize) {
+    temp = realloc(pstack->base, 
+                   pstack->stacksize + (size_t)(STACK_INC_SIZE));
+    if (!temp) {
+      error("Stack in heap cannot extend!");
+      return false;
+    }
+
+    if (temp != pstack->base) {
+      pstack->top  = temp + (pstack->top - pstack->base);
+      pstack->base = temp;
+    }
+    
+    pstack->stacksize += (size_t)(STACK_INC_SIZE);
+  }
+
+  *(pstack->top) = *element;
+  pstack->top++;
+
+  return true; 
+}
+
+	static int
+pop(pstack, element)
+	register struct stack *pstack;
+	register void         **element;
+/* Pop top element from stack.  */
+{
+  if (pstack->top == pstack->base) {
+    error("Stack is empty!");
+    return false;
+  }
+  
+  pstack->top--;
+  *element = *(pstack->top);
+  
+  return true;  
+}
+
+	static void*
+top(pstack)
+	register struct stack *pstack;
+/* Get top element from stack.  */
+{
+   if (pstack->top > pstack->base) {
+     return *(pstack->top - 1);
+   }
+   else
+     return NULL;
+}
+
+	static int
+is_empty(pstack)
+	register struct stack *pstack;
+/* To judge whether the stack is empty.  */
+{
+    return (pstack->top == pstack->base);
+}
 
 	void
 puttree(root, fout)
@@ -556,20 +645,49 @@
 	register FILE *fout;
 /* Output the delta tree with base ROOT in preorder to FOUT.  */
 {
-	struct branchhead const *nextbranch;
-
-	if (!root) return;
-
-	if (root->selector)
-		putdelta(root, fout);
-
-	puttree(root->next, fout);
+  struct hshentry         *node  = NULL;
+  struct branchhead const *nextbranch;
+  struct stack            pstack = { 0 };
+
+  if (!root)
+    return;
+
+  init(&pstack);
+    
+  if (!push(&pstack, &root)) {
+    exit(-1);
+  }
+  
+  while (!is_empty(&pstack)) {
+    if (!pop(&pstack, &node)) {
+      exit(-1);
+    }
+
+    if (node) {
+      if (node->selector)
+        putdelta(node, fout);
+
+      if (node->branches) {
+        for (nextbranch = node->branches; 
+             nextbranch; 
+             nextbranch = nextbranch->nextbranch)
+          if (!push(&pstack, &(nextbranch->hsh))) {
+            exit(-1);
+          }
+      }
+      
+      if (node->next) {
+        if (!push(&pstack, &(node->next))) {
+          exit(-1);
+        }
+      }
+    }
+    else {
+      error("Node is empty!");
+    }
+  }
 
-	nextbranch = root->branches;
-	while (nextbranch) {
-	     puttree(nextbranch->hsh, fout);
-	     nextbranch = nextbranch->nextbranch;
-	}
+  destroy(&pstack);
 }
attach.tar.gz (application/gzip, 736.8 KB) - not displayed