-
Notifications
You must be signed in to change notification settings - Fork 1.9k
linux: a few fixes of callback typecasting (for the upcoming ClangCFI) #12260
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Thanks for giving us a heads up on this so we can make the needed changes. There are likely a few more less common call paths in the code which we'll need to update. If you have easy access to a dedicated test system, and are so inclined, you could probably uncover the rest of these by locally running the ZFS Test Suite. The basic recipe can be found in the .github/workflows/zfs-tests-functional.yml
file in the zfs repository. The test suite does perform some destructive operations, to to be extra safe I'd just make sure not to run it on a system which contains a pool with real data.
module/os/linux/zfs/zpl_file.c
Outdated
} | ||
|
||
static int | ||
zpl_readpage_filler(void *data, struct page *page) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's use pp
for the page variable just for consistency with zpl_readpage()
.
Yeah, I think I could test it with ZFS Suite to catch more. I'll update this PR then, thanks! |
@behlendorf, thanks for the idea, you was right. I've triggered some additional paths during the execution of ZFS Test Suite:
Rough total amount of failures during ZTS cycle:
I'll update this PR till the end of this week by covering the abovementioned code. |
Instead of declaring zio_{,re}execute() as void (*)(zio_t *zio) and then typecasting it to void (*)(void *arg) when adding a taskq, declare it with void *arg initially. This implies no functional changes, but eliminates callback typecasting / type violation, which may be fatal in case of ClangCFI. Signed-off-by: Alexander Lobakin <[email protected]>
Just declare zil_itxg_clean() with void *arg instead of itxs_t * to be able to pass it directly to taskq_dispatch() without typecasting. No functional changes, but critical for passing ClangCFI checks. Signed-off-by: Alexander Lobakin <[email protected]>
To avoid callback typecast violations (and e.g. triggering ClangCFI), declare separate callbacks for read_cache_pages() and zpl_address_space_operations: - convert zpl_readpage() to a static inline zpl_readpage_common(), which now takes only one argument instead of two (the first was never used in the code); - declare zpl_readpage() for zpl_address_space_operations and zpl_readpage_filler() to pass it as read_cache_pages() agrument. They differ only by the type of the first argument (struct file * vs void *), and both just inline zpl_readpage_common(). - Pass NULL as the last argument (void *data) of read_cache_pages() call as zpl_readpage_filler() (former zpl_readpage()) doesn't use it. Signed-off-by: Alexander Lobakin <[email protected]>
Just declare zio_interrupt() as void (*)(void *) to match task_func_t type and pass callback typecheck. This doesn't change anything for the direct callers. Signed-off-by: Alexander Lobakin <[email protected]>
Updated. Now covers several more cases:
and passes the entire ZTS with not a single CFI failure. |
At the moment, nvs_xdr_nvp_op() passes several static inlines as callbacks for xdr_array(). This implicitly creates static functions for each of them to be able to retrieve their addresses. However, all those functions have different prototypes incompatible with xdrproc_t which involves callback typecasting and CFI failures. Just declare the callbacks explicitly via a shorthand helper. This doesn't increase the codesize, but allows to pass function addresses directly to xdr_array(). Signed-off-by: Alexander Lobakin <[email protected]>
Wrap the external iput() into a static callback and pass it to taskq_dispatch() instead of passing it directly with typecasting. Signed-off-by: Alexander Lobakin <[email protected]>
Wrap it into a static function and pass it direcly as a zcp_cleanup_t callback. Signed-off-by: Alexander Lobakin <[email protected]>
Wrap it into a static function and pass it as a task callback instead. ops is dereferenced prior to this, so check for ops != NULL is not needed. Signed-off-by: Alexander Lobakin <[email protected]>
Updated. Those are |
Ping? Was approved 19 days ago... |
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes #12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
* zio: avoid callback typecasting * zil: avoid zil_itxg_clean() callback typecasting * zpl: decouple zpl_readpage() into two separate callbacks * nvpair: explicitly declare callbacks for xdr_array() * linux/zfs_nvops: don't use external iput() as a callback * zcp_synctask: don't use fnvlist_free() as a callback * zvol: don't use ops->zv_free() as a callback for taskq_dispatch() Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Closes openzfs#12260
Motivation and Context
ClangCFI (Control Flow Integrity) for ARM64 was accepted upstream during 5.13 merge window and will be widely available with 5.13 stable release.
ClangCFI for x86 is currently WIP, hopefully will hit 5.14 cycle.
CFI strongly prohibits callbacks typecasting as a part of possible code exploits/attacks.
On default configuration, every callback type mismatch leads to kernel panic, in "permissive" mode (which is designed for development and hunting mismatches/casting) this results in kernel warning splat. This can become a problem when ZFS will receive 5.13+ support.
Typical CFI failure splat in "permissive" mode:
Description
This PR might not cover 100% of callback type mismatches in ZFS kernel code, but at least fixes CFI failures on common tasks (pool/dataset creation/editing, booting Linux from ZFS root etc.).
The actual "fixes" are typecasting removals within as minimal changes as possible.
Covered code / CFI failures:
How Has This Been Tested?
Types of changes
Checklist:
Signed-off-by
.