{"id":17139757,"url":"https://github.com/evincarofautumn/ward","last_synced_at":"2026-02-28T05:41:02.609Z","repository":{"id":66202530,"uuid":"72570599","full_name":"evincarofautumn/Ward","owner":"evincarofautumn","description":"A static analysis tool for C.","archived":false,"fork":false,"pushed_at":"2019-10-29T22:41:37.000Z","size":233,"stargazers_count":28,"open_issues_count":9,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-11-30T02:05:45.059Z","etag":null,"topics":["c","haskell","locking","signals","static-analysis","static-code-analysis"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evincarofautumn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-11-01T19:45:31.000Z","updated_at":"2025-08-16T18:03:20.000Z","dependencies_parsed_at":"2023-03-01T09:16:10.534Z","dependency_job_id":null,"html_url":"https://github.com/evincarofautumn/Ward","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/evincarofautumn/Ward","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evincarofautumn%2FWard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evincarofautumn%2FWard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evincarofautumn%2FWard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evincarofautumn%2FWard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evincarofautumn","download_url":"https://codeload.github.com/evincarofautumn/Ward/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evincarofautumn%2FWard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29925693,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T19:37:42.220Z","status":"online","status_checked_at":"2026-02-28T02:00:07.010Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","haskell","locking","signals","static-analysis","static-code-analysis"],"created_at":"2024-10-14T20:13:00.363Z","updated_at":"2026-02-28T05:41:02.600Z","avatar_url":"https://github.com/evincarofautumn.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ward\n\nA static analysis tool for C.\n\nWard accepts C99 source files, annotated with *permissions*, and verifies that permissions are correct for all top-level functions. A permission is a constraint on the context in which some code is allowed to run, and can be used to verify things like locking, signal-safety, program phases (e.g., initialization and shutdown), and memory allocation (e.g., disallowing allocation in performance- or safety-critical code).\n\nC programs tend to have a fair amount of global state, which can produce subtle errors if used incorrectly. Ward is designed to make it easier to use this sort of state correctly. It’s a fairly liberal analysis—the goal is to call attention to suspicious code, not verify everything precisely.\n\n## Building\n\n[![Build Status](https://travis-ci.org/evincarofautumn/Ward.svg?branch=master)](https://travis-ci.org/evincarofautumn/Ward)\n\nWard is written in Haskell using the [`language-c`](https://hackage.haskell.org/package/language-c) package for parsing C99. You need [Stack](https://docs.haskellstack.org/en/stable/README/) to build it.\n\n```\n\u003e git clone git@github.com:evincarofautumn/Ward.git\n\u003e cd Ward\n\u003e stack setup  # if this is your first time using Stack\n\u003e stack build\n\u003e stack exec ward -- --help\n\nWard - A static analysis tool for C.\n\nUsage: ward CPP [-M|--mode html|compiler|graph] [-C|--config FILE] [-q|--quiet]\n            PATH... [-P|--preprocess FLAG]\n  Verify Ward permissions for all top-level functions.\n\nAvailable options:\n  CPP                      Name of preprocessor.\n  -M,--mode html|compiler|graph\n                           Output mode style (default 'compiler'). The 'graph'\n                           mode does not run analyses, just parses the source\n                           files and emits inferred call graph.\n  -C,--config FILE         Read permission information from configuration FILE.\n  -q,--quiet               Suppress output except for errors.\n  PATH...                  Paths to C source files.\n  -P,--preprocess FLAG     Pass FLAG to preprocessor.\n  -h,--help                Show this help text\n```\n\n`stack install` will place a copy of the executable in `~/.local/bin` (Mac \u0026 Linux) or `%APPDATA%\\local\\bin` (Windows).\n\n## Running\n\nWard accepts a path to a C preprocessor (e.g., `gcc`), a list of paths to C sources, one or more config files, and additional flags to pass to the preprocessor. On OS X, you’ll want to pass `-fno-blocks` or `-U__BLOCKS__` because the C parsing library used by Ward [does not yet support Apple’s block extension](https://github.com/visq/language-c/issues/15). A typical invocation looks like this:\n\n```\nward gcc foo.c bar.c --config=my.config -- -I/some/include/path -U__BLOCKS__\n```\n\nWard should build and run on OS X and Linux; it may work on Windows with a suitable preprocessor installed, but this hasn’t been tested.\n\n## Writing a Config File\n\nWard determines how to carry out its analysis based on *config files* with the following format. You can check out the [grammar of config files](https://github.com/evincarofautumn/Ward/blob/b14945c1f3d7b1dc9b0e387fbcef923790f58541/src/Config.hs#L18) for more details.\n\n\n### Comments\n\n```\n// This is a comment. It begins with two slashes and continues to the end of the line.\n```\n\n### Declarations and Restrictions\n\nIn order to use a permission, you must declare it in the config by specifying its name.\n\n```\nmy_permission;\n```\n\nA permission may be annotated with a *description* string for documentation purposes and to help produce better error messages.\n\n```\nmy_permission \"permission to do the thing\";\n```\n\nAfter the permission name and before the description (if present), you can specify *modifiers* for the permission. Currently the only modifier is `implicit`:\n\n```\nalloc implicit \"can allocate\";\n```\n\nWith the `implicit` modifier, the `alloc` permission will be implicitly granted to all functions unless they explicitly waive it (see “Annotating Your Code”).\n\nPermissions may have any number of additional *restrictions*, which consist of an ASCII rightwards arrow `-\u003e` followed by a Boolean expression using the operators `\u0026` (and), `|` (or), and `!` (not). Restrictions specify that if the permission to the left of the arrow is used, then the context must have or lack some additional permissions specified on the right. Like permissions, restrictions can also have description strings.\n\n```\nlock_held       \"assume the lock is held\";\n\ntake_lock       \"take the lock\"\n  -\u003e !lock_held \"cannot take the lock recursively\";\n```\n\n### Enforcements\n\nWard will only enforce annotations for functions specified in the config, so you can add Ward annotations incrementally to an existing codebase. Enforcement directives begin with `.enforce` and accept:\n\n * A file name, to enforce annotations for all functions declared or defined in that file;\n * A function name, to enforce annotations for a particular function; or\n * Both a file name and function name, to enforce annotations for a single function if defined in the given file—this is useful for `static` functions.\n\n```\n.enforce \"locks.h\";                    // All functions declared in a path ending in \"locks.h\"\n.enforce foo_bar;                      // All functions named \"foo_bar\"\n.enforce \"locks-internal.c\" do_stuff;  // The function \"do_stuff\" in \"locks-internal.c\"\n```\n\n## Annotating Your Code\n\nWard accepts annotations in the form of function-level attributes, which specify permissions and the actions Ward should take with them.\n\n```\n__attribute__((ward(action(perm, perm, …), action(perm, perm, …), …)))\n```\n\nAn *action* may be any of the following:\n\n| Action      | Effect |\n| ----------- | ------ |\n| `need(p)`   | When this function is called, `p` must be in the context. |\n| `use(p)`    | Same as `need`, but expresses the intent that `p` is used directly, and should be subject to restrictions specified in the config. |\n| `deny(p)`   | When this function is called, `p` must *not* be in the context. |\n| `waive(p)`  | If `p` was declared `implicit`, this function is *not* granted `p`. |\n| `grant(p)`  | After this function is called, `p` is added to the context. |\n| `revoke(p)` | After this function is called, `p` is removed from the context. |\n\nHere’s a typical example, where `alloc` is declared `implicit`:\n\n```\n#define PERM(...) __attribute__((ward(__VA_ARGS__)))\n\nvoid must_not_allocate(void)\n  PERM(waive(alloc));\n\nvoid take_lock(void)\n  PERM(need(lock), revoke(lock), grant(locked));\n\nvoid must_have_lock_held(void)\n  PERM(need(locked));\n\nvoid release_lock(void)\n  PERM(need(locked), revoke(locked), grant(lock));\n```\n\nThese annotations enforce the following properties:\n\n* `must_not_allocate`:\n\n  * `waive(alloc)`: this function cannot call functions that implicitly `need(alloc)`\n\n* `take_lock`:\n\n  * `need(lock)`: if a function takes the lock, it must also be annotated with `need(lock)`\n  * `revoke(lock)`: when the lock is held, it can’t be recursively locked\n  * `grant(locked)`: a function annotated with `need(locked)` can only be called when the lock is held\n\n* `must_have_lock_held`:\n\n  * `need(locked)`: the lock must have been taken already before entering this function\n\n* `release_lock`:\n\n  * `need(locked)`: the lock must be held in order to release it\n  * `revoke(locked)`: when the lock is released, a function annotated with `need(locked)` can no longer be called\n  * `grant(lock)`: the lock can be taken again after it is released\n\nBy implicitly granting `alloc`, you only need to annotate those functions that are explicitly *not* allowed to allocate. You can do a similar thing with e.g. `signal_unsafe implicit;`—functions are not required to be signal-safe by default, but you want to make sure that no signal-safe function calls a signal-unsafe function!\n\nIn this way, Ward is pay-as-you-go: you only need to annotate and verify the specific source files and permissions you’re interested in, and you can define whatever meanings you like for a permission, because it’s just a label.\n\n## Known Deficiencies\n\n### Poor Performance\n\nOn a project of nontrivial size (dozens of kloc), Ward consumes a large amount of memory and operates slowly due to retaining a large amount of `language-c` AST information. The preferred method for checking a large project is to build a *call map* for each translation unit *separately* using `--mode=graph`, and then load \u0026 check all the call maps together. You can see an example of this in [`check-mono.sh`](https://github.com/evincarofautumn/Ward/blob/master/check-mono.sh).\n\n### No Indirect Calls\n\nWard currently does not handle indirect calls. In practice this is not a significant limitation, as the vast majority of calls in typical C code are direct, but it may be addressed in the future.\n\n### Limited Testing\n\nIt seems to be usable and effective in practice, but it’s not very well tested. We’re working on adding [a comprehensive test suite](https://github.com/evincarofautumn/Ward/tree/master/test).\n\n### No Complex Control Flow\n\nIt also doesn’t handle non-trivial control flow very well, so you may need to restructure some code in order to make it checkable. For example, Ward cannot verify this, because the `locked` permission is conditional:\n\n```\nvoid foo(bool lock) {\n  if (lock) {\n    take_lock();\n  }\n  …\n  if (lock) {\n    release_lock();\n  }\n}\n```\n\nWard doesn’t know whether the `if` branch will be taken, so it conservatively assumes that it requires the permission to lock. This may be addressed in the future to support more coding patterns. However, as a workaround, currently it can verify this alternative formulation:\n\n```\nvoid foo_locked(void) {\n  take_lock();\n  foo_unlocked();\n  release_lock();\n}\n\nvoid foo_unlocked(void) {\n  …\n}\n```\n\n(And this is arguably better coding practice, anyway.)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevincarofautumn%2Fward","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevincarofautumn%2Fward","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevincarofautumn%2Fward/lists"}