Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CN specs to MKM #142

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open

Conversation

podhrmic
Copy link
Collaborator

@podhrmic podhrmic commented Dec 2, 2024

This PR adds CN functional specifications to the MKM code in client.c, as well as various supporting files. These specifications are intended to be run-time tested as well as verified.

Files:

  • client.c - target function definitions
  • client.h - types, and CN predicates defining the types
  • cn_stubs.h - specifications and redefinitions for various library functions that can't be supported directly
  • run-cn-test.sh - run the CN test generator using the necessary flags
  • process-cn-test.sh - run the preprocessor. Called by run-cn-test.sh

Unfortunately, the CN verifier and test generators require different code-level annotations at various points. We handle this using a pair of macros:

  • CN_ENV - set when calling CN in either verify or test modes
  • CN_TEST - set when calling CN in test mode

Testing the PR

To run the verifier, run:

$ make cn_proof

Note this may be quite slow. It takes around 3m30s to complete on my newish MBP.

To run the tests, run:

$ ./run-cn-test.sh client.c test_directory 

Outstanding bugs

  • The run-cn-test.sh script pauses during execution and only continues when I press return. I assume this is due to some call in client.c which is blocking, but I haven't figured out the culprit yet. FIXED
  • Various points in the code include TODO notes. These generally indicate places I'm working around a limitation in CN of some kind. But some of these might be possible to resolve.

Changes:

  • CN specs for memory safety
  • CN specs for functional correctness
  • Scripts for executing the run-time testing capabilities
  • Integration of proofs into CI
  • Integration of testing into CI
  • Fix bugs / limitations (see above)

Base automatically changed from 98-mkm-server to main December 11, 2024 00:59
@septract septract force-pushed the 139-feature-verify-test-mkm-module branch from 667e47c to a4f9a2a Compare December 18, 2024 21:56
@septract septract force-pushed the 139-feature-verify-test-mkm-module branch from a4f9a2a to 218a2f2 Compare December 18, 2024 22:42
@podhrmic podhrmic linked an issue Dec 31, 2024 that may be closed by this pull request
4 tasks
@septract
Copy link

@peterohanley @podhrmic PR ready to be re-reviewed when you have time. We now supports verification and functional correctness proofs of MKM, and testing of MKM. I've removed a lot of the limitations that existed before (but introduced some new ones caused by the test generator...).

@septract septract mentioned this pull request Jan 1, 2025
Copy link
Contributor

@peterohanley peterohanley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to failing malloc are big improvements.

@spernsteiner
Copy link
Contributor

I tried this on Linux (running ./run-cn-test.sh client.c) and got an error about _IO_FILE:

In file included from ./client-gen_gen.h:6,
                 from ./client-gen_test.c:1:
./cn.h:18:8: error: redefinition of ‘struct _IO_FILE’
   18 | struct _IO_FILE {
      |        ^~~~~~~~
In file included from /usr/include/stdio.h:43,
                 from /home/spernsteiner/.opam/cn/lib/cn/runtime/include/cn-executable/utils.h:4,
                 from /home/spernsteiner/.opam/cn/lib/cn/runtime/include/cn-testing/alloc.h:6,
                 from /home/spernsteiner/.opam/cn/lib/cn/runtime/include/cn-testing/prelude.h:7,
                 from ./client-gen_gen.h:4:
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h:49:8: note: originally defined here
   49 | struct _IO_FILE
      |        ^~~~~~~~

It seems like cn test copies the definition of struct _IO_FILE from our wars.h into a generated file cn.h, then includes both that file and the system-wide stdio.h, causing a conflict between the two definitions of struct _IO_FILE. However, just removing the definition from wars.h causes a CN error about struct _IO_FILE being undefined - CN's stdio.h mentions the struct via typedef struct _IO_FILE FILE;, but never defines it anywhere, thus triggering rems-project/cerberus#437.

I was able to work around this by patching MKM to not use stdio.h when building under CN, and then removing the definition of struct _IO_FILE from wars.h:

Click to see diff
diff --git a/components/include/wars.h b/components/include/wars.h
index f8f476c..81e230f 100644
--- a/components/include/wars.h
+++ b/components/include/wars.h
@@ -77,11 +77,4 @@
 // accepts this but CN rejects it
 #define WAR_CN_437 1
 
-#ifdef WAR_CN_437
-// this is required so we can mention FILE* parameters
-struct _IO_FILE {
-    int x;
-};
-#endif
-
 #endif // __CN_WARS_H__
diff --git a/components/mission_key_management/client.c b/components/mission_key_management/client.c
index f9e4426..71690d8 100644
--- a/components/mission_key_management/client.c
+++ b/components/mission_key_management/client.c
@@ -8,7 +8,6 @@
 
 #include <sys/socket.h>
 #include <unistd.h>
-#include <stdio.h>
 
 //SYSTEM_HEADERS
 // ^^^ magic string used during preprocessing - do not move / remove 
@@ -18,9 +17,12 @@
 #if ! defined(CN_ENV)
 # include <sys/types.h>
 # include <sys/epoll.h>
+# include <stdio.h>
+# define LOG(...)   (fprintf(stderr, __VA_ARGS__))
 #else  
 # include "cn_stubs.h"
 # include "cn_array_utils.h"
+# define LOG(...)   /* nothing */
 #endif
 
 // TODO `Alloc` construct not supported by `cn test`
@@ -445,11 +447,11 @@ $*/
                     c->response, c->response + MEASURE_SIZE);
             if (c->key == NULL) {
                 // No matching key was found for this request.
-                fprintf(stderr, "client %d: error: bad request for key %u\n", c->fd, c->key_id[0]);
+                LOG("client %d: error: bad request for key %u\n", c->fd, c->key_id[0]);
                 return RES_ERROR;
             }
             client_change_state(c, CS_SEND_KEY);
-            fprintf(stderr, "client %d: sending key %u\n", c->fd, c->key_id[0]);
+            LOG("client %d: sending key %u\n", c->fd, c->key_id[0]);
             break;
 
         case CS_SEND_KEY:
diff --git a/components/mission_key_management/cn_stubs.h b/components/mission_key_management/cn_stubs.h
index 17025ea..facafbd 100644
--- a/components/mission_key_management/cn_stubs.h
+++ b/components/mission_key_management/cn_stubs.h
@@ -80,10 +80,6 @@ $*/
 
 // From `stdio.h`
 
-/*$ spec fprintf(pointer f, pointer s);
-requires true;
-ensures true;
-$*/
 #if defined(CN_TEST)
 # define fprintf(...) 0 
 #endif 
@@ -129,14 +125,7 @@ $*/
 # define read(f,b,c) _read_uint8_t(f,b,c)
 #else 
 ssize_t _read_mock(void *buf, size_t count) {
-    // Fake file descriptor for use during testing 
-    FILE *file = tmpfile();
-    if (!file) {
-        perror("tmpfile failed (client_read)");
-        return -1;
-    }
-    int tmp_fd = fileno(file);
-    return read(tmp_fd, buf, count); 
+    return 0;
 }
 #define read(f,b,c) _read_mock(b,c)
 #endif 
@@ -155,14 +144,7 @@ $*/
 # define write(f,b,c) _write_uint8_t(f,b,c)
 #else 
 ssize_t _write_mock(const void *buf, size_t count) { 
-    // Fake file descriptor for use during testing 
-    FILE *file = tmpfile();
-    if (!file) {
-        perror("tmpfile failed (client_read)");
-        return -1;
-    }
-    int tmp_fd = fileno(file);
-    return write(tmp_fd, buf, count); 
+    return count;
 }
 #define write(f,b,c) _write_mock(b,c)
 #endif 

@podhrmic
Copy link
Collaborator Author

podhrmic commented Jan 7, 2025

@spernsteiner that is a good catch. We will need to add a CI job to run the test generation, which will probably show a couple of other issues.

@podhrmic podhrmic closed this Jan 7, 2025
@podhrmic podhrmic reopened this Jan 7, 2025
@podhrmic podhrmic marked this pull request as ready for review January 9, 2025 22:57
Copy link
Collaborator Author

@podhrmic podhrmic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Minor comments above. Still need to add a CI job that runs the tests.
What do you think about linking the remaining TODOs to CN tickets, such that we known what needs to be fixed?

@@ -2,6 +2,18 @@
#define CN_ARRAY_UTILS_H_

/*$
predicate (map<u64,u8>) Array_Owned_u8 (pointer p, u64 l)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a similar predicate - did you mean to use this one?

return Arr;
}

predicate (map<u64,u8>) Array_Block_u8 (pointer p, u64 l)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

@@ -69,4 +69,31 @@ ensures
take i = each(u64 j; j >= 0u64 && j < n) {Block<uint8_t>(array_shift<uint8_t>(return, j))};
$*/

/*$
datatype OptionMemory {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have at least a sentence of description for each predicate, similar to what Gus did for array_utils.h. I know this might be obvious to you, but this will really pay off during the transition. Maybe @peterohanley can help here?

# define NULL ((void *)0)
#endif

/* TODO list:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming you are still actively working on this TODO?
Also, this list should be a part of the PR description rather than being hidden in the code.

SomeClientObject {struct client obj},
NoneClientObject {}
}
predicate (OptionClientObject) OptionClientNew(pointer p, i32 fd, i32 state)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it useful to have the predicates in a separate file?

// Pure predicate representing valid states of `enum client_state`.
// CN could easily generate this automatically (see #796)
/*$
function (boolean) ValidState (u32 state) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the comments!

/*
start:
┌────────────────┐
┌─►│ CS_RECV_KEY_ID ├──────┐
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice diagram!

ensures true;
$*/
#if defined(CN_TEST)
# define fprintf(...) 0
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these should defines really be in some sort of CN standard library...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Verify / test MKM module
4 participants