diff --git a/poc/maxLengthPassword.txt b/poc/maxLengthPassword.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ac3db58e375af73715029899647471a332d2dd26
--- /dev/null
+++ b/poc/maxLengthPassword.txt
@@ -0,0 +1 @@
+put 1 8 888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
diff --git a/src/maxLengthPassword/passbook.c b/src/maxLengthPassword/passbook.c
new file mode 100644
index 0000000000000000000000000000000000000000..9572426738a4dff6be70a5c40d14082def2e14a1
--- /dev/null
+++ b/src/maxLengthPassword/passbook.c
@@ -0,0 +1,594 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+#ifdef PASSBOOK_LIBFUZZER
+#include <stdint.h>
+const char LIBFUZZER_INPUT_FILE[] = "libFuzzerInput.tmp";
+/* turn off tracing to make it run faster */
+#define printf(...)
+#define fprintf(...)
+#endif
+
+const char INSTRUCTION_PUT[] = "put";
+
+const char INSTRUCTION_REM[] = "rem";
+
+const char INSTRUCTION_GET[] = "get";
+
+const char INSTRUCTION_SAVE[] = "save";
+
+const char INSTRUCTION_LIST[] = "list";
+
+const char INSTRUCTION_MASTERPW[] = "masterpw";
+
+/* a credential is a username/password pair */
+typedef struct {
+  char * username;
+  char * password;
+} cred_t;
+
+/* we store a mapping from URLs to credentials using a binary tree
+   to try to ensure log lookup performance */
+typedef struct node {
+  char * url;
+  cred_t cred;
+  struct node *left;
+  struct node *right;
+} node_t;
+
+static const node_t * lookup(const node_t *p, const char *url){
+  while (p != NULL){
+    int ret = strcmp(url,p->url);
+    if (ret == 0){
+      return p;
+    }else if (ret < 0){
+      p = p->left;
+    }else{
+      p = p->right;
+    }
+  }
+  return p; // not found
+}
+#define VALID_PASSWORD_LENGTH 1014
+static void node_print(const node_t *p){
+  printf("URL: %s, Username: %s, Password: %s\n",p->url,p->cred.username,p->cred.password);
+}
+
+/* construct a new node */
+static node_t *node_new(const char *url, const cred_t cred){
+  node_t *new = malloc(sizeof(node_t));
+  assert(new != NULL && "new: malloc failed");
+  new->url = strdup(url);
+  assert(new->url != NULL && "new: strdup url failed");
+  new->cred.username = strdup(cred.username);
+  assert(new->cred.username != NULL && "new: strdup username failed");  
+
+  // new->cred.password = strdup(cred.password);
+  new->cred.password = (char *)malloc(sizeof(char) * VALID_PASSWORD_LENGTH);
+  assert(new->cred.password != NULL && "malloc password failed");
+  strcpy(new->cred.password, cred.password);
+
+  new->left = NULL;
+  new->right = NULL;
+  return new;
+}
+
+/* updates a node's credential in place: 
+   replaces p's credential with that from q and frees q */
+static void node_edit_cred(node_t * p, node_t *q){
+  free(p->cred.username);
+  free(p->cred.password);
+
+  p->cred.username = q->cred.username;
+  p->cred.password = q->cred.password;
+  free(q->url);
+  free(q);
+}
+
+static void node_free(node_t *p){
+  free(p->url);
+  free(p->cred.username);
+  free(p->cred.password);
+  free(p);
+}
+
+/* insert q into p
+   we assume that if q has children then it cannot already
+   be present in p. Otherwise, if q has no children and we find its url in p,
+   then we edit the found entry in place while preserving its children */
+static node_t * node_insert(node_t *p, node_t *q){
+  if (p == NULL){
+    return q;
+  }
+  if (q == NULL){
+    return p;
+  }
+  /* we store a pointer to a node pointer that remembers where in the 
+     tree the new node needs to be added */
+  node_t ** new = NULL;
+  node_t * const start = p;
+  while (new == NULL) {
+    int ret = strcmp(q->url,p->url);
+    if (ret == 0){
+      assert (q->left == NULL && q->right == NULL && "illegal insertion");
+      /* edit the node in place */
+      node_edit_cred(p,q);
+      /* q is now freed so cannot be used anymore */
+      return start;
+    }else if (ret < 0){
+      if (p->left == NULL){
+        new = &(p->left);
+      }else{
+        p = p->left;
+      }
+    }else{
+      if (p->right == NULL){
+        new = &(p->right);
+      }else{
+        p = p->right;
+      }
+    }
+  }
+  *new = q;
+  return start;
+}
+
+/* returns a pointer to the tree with the node added or with the existing
+   node updated if it was  already present */
+static node_t * put(node_t *p, const char *url, const cred_t cred){
+  return node_insert(p,node_new(url,cred));
+}
+
+/* destroy tree rooted at p */
+static void destroy(node_t *p){
+  while (p != NULL){
+    node_t * left = p->left;
+    node_t * const right = p->right;
+    left = node_insert(left,right);
+    node_free(p);
+    p = left;
+  }
+}
+
+/* returns a pointer to the tree with the node removed (if it was present) */
+static node_t * rem(node_t *p, const char *url){
+  node_t * const start = p;
+  /* remember where the pointer to p was stored */
+  node_t ** pptr = NULL;
+  while (p != NULL){
+    int ret = strcmp(url,p->url);
+    if (ret == 0){
+      node_t * left = p->left;
+      node_t * const right = p->right;
+      left = node_insert(left,right);
+      node_free(p);
+      if (pptr != NULL){
+        *pptr = left;
+        return start;
+      }else{
+        /* p was the only node in the tree */
+        assert(p == start);
+        return left;
+      }
+    }else if (ret < 0){
+      pptr = &(p->left);
+      p = p->left;
+    }else{
+      pptr = &(p->right);
+      p = p->right;
+    }
+  }
+  return start; // not found
+}
+
+const char WHITESPACE[] = " \t\r\n";
+
+
+/* tokenise a string, splitting on characters in WHITESPACE, up to
+ * a maxium of toksLen tokens, each of whose start addresses is put into
+ * toks and each of which is NUL-terminated in str.
+ * returns number of tokens found */
+unsigned int tokenise(char *str, char * toks[], unsigned int toksLen){
+  unsigned numToks = 0;
+  while (numToks < toksLen){
+    /* strip leading whitespace */     
+    size_t start = strspn(str,WHITESPACE);
+    if (str[start] != '\0'){
+      toks[numToks] = &(str[start]);    
+    
+      /* compute the length of the token */
+      const size_t tokLen = strcspn(toks[numToks],WHITESPACE);
+      if (tokLen > 0){
+        toks[numToks][tokLen] = '\0';
+        str = &(toks[numToks][tokLen+1]);
+        numToks++;
+      }else{
+        return numToks;
+      }
+    }else{
+      return numToks;
+    }
+  }
+  return numToks;
+}
+
+#define MAX_LINE_LENGTH  1022
+#define MAX_INSTRUCTIONS 1024
+/* two extra chars in each line: the newline '\n' and NUL '\0' */
+#define INSTRUCTION_LENGTH (MAX_LINE_LENGTH+2)
+
+
+/* a global instruction buffer */
+char inst[INSTRUCTION_LENGTH];
+
+/* password mapping for each url: initially empty */
+node_t * map = NULL;
+
+/* a doubly-linked list of node pointers
+   is used to implement stacks/queues of nodes so we can implement various
+   tree traversal algorithms without using recursion (to avoid stack overflow
+   for very large trees). Stack overflow is a trivial form of memory-safety
+   vulnerability. */
+typedef struct nodeptr_list_elem {
+  const node_t *p;
+  struct nodeptr_list_elem *next;
+  struct nodeptr_list_elem *prev;
+} nodeptr_list_elem_t;
+
+typedef struct nodeptr_list {
+  nodeptr_list_elem_t *head;
+  nodeptr_list_elem_t *last;
+} nodeptr_list_t;
+
+
+/* push an element p onto the front of a nodeptr list lst */
+nodeptr_list_t list_push(nodeptr_list_t lst, const node_t *p){
+  nodeptr_list_elem_t *n = malloc(sizeof(nodeptr_list_elem_t));
+  assert(n != NULL && "push: malloc failed");
+  n->p = p;
+  n->next = lst.head;
+  n->prev = NULL;  
+  if (lst.head != NULL){
+    assert(lst.last != NULL);
+    lst.head->prev = n;
+  }else{
+    assert(lst.last == NULL);
+    lst.last = n;
+  }
+  lst.head = n;
+  
+  return lst;
+}
+
+/* when out is non-NULL we place a pointer to the first node into it.
+   assumption: lst.head and lst.last are non-NULL */
+nodeptr_list_t list_pop(nodeptr_list_t lst, const node_t **out){
+  assert(lst.head != NULL && lst.last != NULL);
+  if (out != NULL){
+    *out = lst.head->p;
+  }
+  if (lst.last == lst.head){
+    free(lst.head);
+    lst.head = NULL;
+    lst.last = NULL;
+  }else{
+    nodeptr_list_elem_t *ret = lst.head->next;
+    free(lst.head);
+    lst.head = ret;
+  }
+  return lst;
+}
+
+/* when out is non-NULL we place a pointer to the last node into it.
+   assumption: lst.head and lst.last are non-NULL */
+nodeptr_list_t list_dequeue(nodeptr_list_t lst, const node_t **out){
+  assert(lst.head != NULL && lst.last != NULL);
+  if (out != NULL){
+    *out = lst.last->p;
+  }
+
+  if (lst.last == lst.head){
+    free(lst.head);
+    lst.head = NULL;
+    lst.last = NULL;
+  }else{
+    nodeptr_list_elem_t *ret = lst.last->prev;
+    free(lst.last);
+    lst.last = ret;
+  }
+  return lst;
+}
+
+/* in order traversal to print out nodes in sorted order. Is used to
+   implement listing of all entries in the passbook */
+void print_inorder(const node_t *p){
+  nodeptr_list_t lst = {.head = NULL, .last = NULL};
+  if (p != NULL){
+    lst = list_push(lst,p);
+
+    while(lst.head != NULL){
+      // keep recursing left until we can go no further
+      while (p->left != NULL){
+        lst = list_push(lst,p->left);
+        p = p->left;
+      }
+      
+      // pop from the stack to simulate the return
+      const node_t *q;
+      lst = list_pop(lst,&q);
+
+      // print the node following the return
+      node_print(q);
+
+      // simulate right recursive call
+      if (q->right != NULL){
+        lst = list_push(lst,q->right);
+        p = q->right;
+      }
+    }
+  }
+}
+
+/* save a node to the given file. We save to the file a "put" instruction
+   that will cause the node to be placed back into the passbook when the
+   file is read. */
+void node_save(const node_t *p, FILE *f){
+  fprintf(f,"%s",INSTRUCTION_PUT);
+  fprintf(f," ");
+  fprintf(f,"%s",p->url);
+  fprintf(f," ");  
+  fprintf(f,"%s",p->cred.username);  
+  fprintf(f," ");  
+  fprintf(f,"%s",p->cred.password);
+  fprintf(f,"\n");
+}
+
+/* save the master password to the given file. We save a "masterpw" 
+   instruction that will cause the passbook to prompt the user for the
+   given master password the next time the file is read */
+void masterpw_save(const char *pw, FILE *f){
+  fprintf(f,"%s",INSTRUCTION_MASTERPW);
+  fprintf(f," ");
+  fprintf(f,"%s",pw);
+  fprintf(f,"\n");
+}
+
+/* level order (i.e. breadth-first) traversal to print nodes out in the
+   order that they need to be put back in to an empty tree to ensure
+   that the resulting tree has the same structure as the original one.
+   This is how we save the passbook to a file.
+   Returns 0 on success; nonzero on failure */
+int save_levelorder(const node_t *p, const char *masterpw,
+                    const char * filename){
+#ifdef PASSBOOK_FUZZ
+  // ignore the file name when fuzzing etc. to avoid DoS on the server
+  FILE *f = fopen("/dev/null","w");
+#else
+  FILE *f = fopen(filename,"w");
+#endif
+  if (f == NULL){
+    fprintf(stderr,"Couldn't open file %s for writing.\n",filename);
+    return -1;
+  }
+  masterpw_save(masterpw,f);
+  nodeptr_list_t lst = {.head = NULL, .last = NULL};
+  if (p != NULL){
+    lst = list_push(lst,p);
+
+    while(lst.last != NULL){
+      lst = list_dequeue(lst,&p);
+      node_save(p,f);
+      if (p->left != NULL){
+        lst = list_push(lst,p->left);
+      }
+      if (p->right != NULL){
+        lst = list_push(lst,p->right);
+      }
+    }
+  }
+  fclose(f);
+  return 0;
+}
+
+/* returns 0 on successful execution of the instruction in inst */
+static int execute(void){
+  char * toks[4]; /* these are pointers to start of different tokens */
+  const unsigned int numToks = tokenise(inst,toks,4);
+    
+  if (numToks == 0){
+    /* blank line */
+    return 0;
+  }
+    
+  if (strcmp(toks[0],INSTRUCTION_GET) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_GET,numToks-1);
+      return -1;
+    }
+    debug_printf("Looking up: %s\n",toks[1]);
+    const node_t *p = lookup(map,toks[1]);
+    if (p != NULL){
+      node_print(p);
+    }else{
+      printf("Not found.\n");
+    }
+
+  } else if (strcmp(toks[0],INSTRUCTION_REM) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_REM,numToks-1);
+      return -1;
+    }
+    debug_printf("Removing: %s\n",toks[1]);
+    map = rem(map,toks[1]);
+    
+  } else if (strcmp(toks[0],INSTRUCTION_PUT) == 0){
+    if (numToks != 4){
+      debug_printf("Expected 3 arguments to %s instruction but instead found %u\n",INSTRUCTION_PUT,numToks-1);
+      return -1;
+    }
+    cred_t cred;
+    cred.username = toks[2];
+    cred.password = toks[3];
+    map = put(map,toks[1],cred);
+
+  } else if (strcmp(toks[0],INSTRUCTION_SAVE) == 0){
+    if (numToks != 3){
+      debug_printf("Expected 2 arguments to %s instruction but instead found %u\n",INSTRUCTION_SAVE,numToks-1);
+      return -1;
+    }
+    debug_printf("Saving under master password %s to file: %s\n",toks[1],toks[2]);
+    if (save_levelorder(map,toks[1],toks[2]) != 0){
+      debug_printf("Error saving to file %s\n",toks[2]);
+      return -1;
+    }
+
+  } else if (strcmp(toks[0],INSTRUCTION_MASTERPW) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_MASTERPW,numToks-1);      return -1;
+    }
+    // when fuzzing (or gathering coverage stats, etc.) don't check master pw
+#ifndef PASSBOOK_FUZZ
+    const char * pass = getpass("Enter master password: ");
+    if (pass == NULL || strcmp(pass,toks[1]) != 0){
+      fprintf(stderr,"Master password incorrect!\n");
+      exit(1); // exit immediately
+    }
+#else
+    return -1; 
+#endif    
+
+  } else if (strcmp(toks[0],INSTRUCTION_LIST) == 0){
+    if (numToks != 1){
+      debug_printf("Expected 0 arguments to %s instruction but instead found %u\n",INSTRUCTION_LIST,numToks-1);
+      return -1;
+    }
+    print_inorder(map);
+
+  }else{
+    debug_printf("Unrecognised instruction %s\n",toks[0]);
+    return -1;
+  }
+  
+  return 0;
+}
+
+/* returns >=0 on success, in which case the number of instructions executed
+   is returned. Returns < 0 on failure. */
+static int run(FILE *f){
+  assert(f != NULL);
+  
+  int instructionCount = 0;
+  while (instructionCount < MAX_INSTRUCTIONS){
+    memset(inst,0,sizeof(inst));
+    char * res = fgets(inst,sizeof(inst),f);
+    if (res == NULL){
+      if (feof(f)){
+        /* end of file */
+        return instructionCount;
+      }else{
+        debug_printf("Error while reading, having read %d lines\n",instructionCount);
+        return -1;
+      }
+    }
+    if (inst[MAX_LINE_LENGTH] != '\0'){
+      if (!(inst[MAX_LINE_LENGTH] == '\n' && inst[MAX_LINE_LENGTH+1] == '\0')){
+        fprintf(stderr,"Line %d exceeds maximum length (%d)\n",instructionCount+1,MAX_LINE_LENGTH);
+        debug_printf("(Expected at array index %d to find NUL but found '%c' (%d))\n",MAX_LINE_LENGTH,inst[MAX_LINE_LENGTH],inst[MAX_LINE_LENGTH]);
+        return -1;
+      }
+    }else{
+      /* inst[MAX_LINE_LENGTH] == '\0', so
+         strlen is guaranteed to be <= MAX_LINE_LENGTH
+         Check if it has a newline and add it if it needs it */
+      size_t len = strlen(inst);
+      if (len > 0){
+        if (inst[len-1] != '\n'){
+          inst[len] = '\n';
+          inst[len+1] = '\0';
+        }
+      }
+    }
+    instructionCount++;
+    int r = execute();
+    if (r != 0){
+      return -1;
+    }
+  }
+
+  if (feof(f)){
+    /* final line of file didn't have a trailing newline */
+    return instructionCount;
+  }else{
+    /* see if we are at end of file by trying to do one more read.
+       this is necessary if the final line of the file ends in a 
+       newline '\n' character */
+    char c;
+    int res = fread(&c,1,1,f);
+    if (res == 1){
+      fprintf(stderr,"Number of instructions (lines) in file exceeds max (%d)\n",MAX_INSTRUCTIONS);
+      return -1;
+    }else{
+      if (feof(f)){
+        /* final read found the EOF, so all good */
+        return instructionCount;
+      }else{
+        /* probably won't ever get here */
+        debug_printf("Error while trying to test if line %d was empty\n",instructionCount+1);
+        return -1;
+      }
+    }
+  }
+}
+
+#ifdef PASSBOOK_LIBFUZZER
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  FILE *f = fopen(LIBFUZZER_INPUT_FILE,"w");
+  fwrite(Data,Size,1,f);
+  fclose(f);
+  f = fopen(LIBFUZZER_INPUT_FILE,"r");
+  run(f);
+  fclose(f);
+  destroy(map);
+  map = NULL;
+  return 0; /* libFuzzer wants 0 returned always */
+}
+#else
+int main(const int argc, const char * argv[]){
+  if (argc <= 1){
+    fprintf(stderr,"Usage: %s file1 file2 ...\n",argv[0]);
+    fprintf(stderr,"       use - to read from standard input\n");
+    exit(0);
+  }
+  
+  for (int i = 1; i<argc; i++){
+    printf("Running on input file %s\n",argv[i]);
+    FILE *f;
+    if (strcmp(argv[i],"-") == 0){
+      f = stdin;
+    }else{
+      f = fopen(argv[i],"r");
+      if (f == NULL){
+        fprintf(stderr,"Error opening %s for reading\n",argv[i]);
+        destroy(map);
+        exit(1);
+      }
+    }
+    int ans = run(f);
+    if (ans < 0){
+      fprintf(stderr,"Error\n");
+    }
+    /* do not close stdin */
+    if (f != stdin){
+      fclose(f);
+    }
+  }
+  destroy(map);
+  return 0;
+}
+#endif