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 Windows support to OpenZFS #14034

Closed
wants to merge 3 commits into from
Closed

Conversation

lundman
Copy link
Contributor

@lundman lundman commented Oct 16, 2022

Motivation and Context

This PR contains the required changed to add Windows as a platform, if that
is desirable.

Description

Windows uses CMake environment, and does not touch any of the Unix build system.

How Has This Been Tested?

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New "feature" (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair, libuutil and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist:

@rkitover
Copy link
Contributor

Hey this is amazing, I was just thinking a few hours ago how awesome it would be if openzfs for windows started integrating with this repo.

I was also thinking looking at this PR, have you heard of cccl?

I've tried this with autotools and it works amazingly well. I could work on adding this support to the existing autotools system once this PR stabilizes. Would you be in favor of that? Not as a replacement for cmake, but as an extension to the existing config system.

@lundman
Copy link
Contributor Author

lundman commented Oct 17, 2022

cccl does look interesting, and could be worth looking into. There are still advantages in using visual studio projects, especially with remote kernel debugging, but as we can't expect Unix developers add source files to VS project files, it needed to be something more easily edited, hence cmake. But these things have evolved in the past, in this project, so it certainly could change again.

@ryao
Copy link
Contributor

ryao commented Oct 17, 2022

This made me realize that the amount of code duplication we have between platforms is ridiculous. That should be cleaned up (although it does not block this PR).

I know this is a draft, but I could not help peek at it and made a few comments on things that stood out to me.

@lundman lundman changed the title Add support for Windows Add Windows support to OpenZFS Oct 18, 2022
@lundman
Copy link
Contributor Author

lundman commented Oct 18, 2022

It's amusing, I'm pleased how little code duplication there is, now. Compared to when we did the first mac port :)

Thanks for glancing at it. The death after bookmark/setup test is suspicious, so that is next to look into.

include/sys/arc_impl.h Outdated Show resolved Hide resolved
include/sys/fs/zfsdi.h Outdated Show resolved Hide resolved
include/sys/zfs_debug.h Show resolved Hide resolved
include/sys/zfs_ioctl.h Outdated Show resolved Hide resolved
@@ -40,7 +40,6 @@
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the "remove strings.h" missed a few, which could be its own PR

module/icp/illumos-crypto.c Show resolved Hide resolved
module/lua/setjmp/setjmp_x86_64.S Outdated Show resolved Hide resolved
@@ -719,6 +730,14 @@ zfs_prop_init(void)
PROP_READONLY, ZFS_TYPE_DATASET, "KEYGUID", B_TRUE, sfeatures);
zprop_register_hidden(ZFS_PROP_REDACTED, "redacted", PROP_TYPE_NUMBER,
PROP_READONLY, ZFS_TYPE_DATASET, "REDACTED", B_FALSE, sfeatures);
#ifdef _WIN32
zprop_register_string(ZFS_PROP_DRIVELETTER, "driveletter", "-",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

PROPs must always be defined on all platforms, remove #ifdef

module/zfs/arc.c Outdated Show resolved Hide resolved
module/zfs/spa_misc.c Outdated Show resolved Hide resolved
@behlendorf behlendorf added the Status: Code Review Needed Ready for review and testing label Oct 21, 2022
lib/libzpool/kernel.c Outdated Show resolved Hide resolved
Copy link
Contributor

@mcmilk mcmilk left a comment

Choose a reason for hiding this comment

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

Hello,

I just found some things - maybe you can review these?
But the main thing looks okay - great work.


#ifdef _WIN32
#include <termios.h>
#endif
#include "zpool_util.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add here a new line again. I think, it was there because of: <system-stuff.h> and "own-stuff.h"

cmd/zpool/zpool_vdev.c Outdated Show resolved Hide resolved
@@ -1,3 +1,3 @@
dist_noinst_DATA += %D%/taskqlatency.bt %D%/zfs-trace.sh

SHELLCHECKSCRIPTS += %D%/zfs-trace.sh
SHELLCHECKSCRIPTS += %D%/zfs-trace.sh
Copy link
Contributor

Choose a reason for hiding this comment

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

There should be a new line ?

lib/libzfs/os/windows/libzfs_mount_os.c Show resolved Hide resolved
lib/libzfs_core/libzfs_core.c Show resolved Hide resolved
module/icp/illumos-crypto.c Show resolved Hide resolved
module/zfs/dsl_pool.c Outdated Show resolved Hide resolved
@lundman lundman force-pushed the windows branch 3 times, most recently from 5eed807 to 8406c93 Compare March 7, 2023 09:45
LOGGER_SESSION, OPEN_ZFS_GUID, flags.c_str(),
levels.c_str(), size_in_mb, etl_file.c_str());

ret = system(command);

Check failure

Code scanning / CodeQL

Uncontrolled data used in OS command

This argument to an OS command is derived from [user input (a command-line argument)](1), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](1), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](1), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](1), dangerously concatenated into [call to operator+](3), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](4), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](4), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](4), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](4), dangerously concatenated into [call to operator+](3), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](5), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](5), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](5), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](5), dangerously concatenated into [call to operator+](3), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](6), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](6), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](6), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](6), dangerously concatenated into [call to operator+](3), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](7), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](7), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](7), dangerously concatenated into [sprintf_s output argument](2), and then passed to system(_Command). This argument to an OS command is derived from [user input (a command-line argument)](7), dangerously concatenated into [call to operator+](3), and then passed to system(_Command).
Copy link
Contributor

Choose a reason for hiding this comment

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

I had originally looked at this from my ipad and could not see the context due to the iPad being slow, so I made a (now deleted) remark that I now see was wrong.

Anyway, the reason that CodeQL complains here is that command injection is possible by altering the arguments passed to the installer and in general, command injection is something to be avoided whenever possible. Here are some references:

https://codeql.github.com/codeql-query-help/cpp/cpp-command-line-injection/
https://wiki.sei.cmu.edu/confluence/display/c/STR02-C.+Sanitize+data+passed+to+complex+subsystems

That said, I do not know the right way to sanitize this offhand, but the whitelist example by Wietse Venema at the second URL looks like a sane solution.

@lundman lundman force-pushed the windows branch 6 times, most recently from 766f5f1 to cc80ea4 Compare March 9, 2023 01:41
NTSTATUS saveStatus;
XSTATE_SAVE SaveState;
#endif

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer it if we put these into zio_abd_checksum_data_t and put boolean_t call_kfpu; into fletcher_4_ops_t. The thing that I dislike about changing fletcher_4_ctx to a struct and passing it to kfpu_begin_ctx() and kfpu_end_ctx(). ties those functions to fletcher_4_ctx, when they are general purpose functions that should be usable for far more than fletcher4.

I guess another way would be to put these members before the anonymous union, make a structure for these members and use a void pointer to do casting, but I like your boolean_t call_kfpu; idea from the other day.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just a stop-gap until, hopefully, the new implementation @AttilaFueloep is working on merges. It is a separate commit, so I can easily drop it when that time comes.

Copy link
Contributor

Choose a reason for hiding this comment

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

So there seems to be agreement that the refactoring I suggested yesterday is a good idea? If so, I'll prepare a PR tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If my vote counts, I'm all for it.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks good to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

@lundman
Copy link
Contributor Author

lundman commented Mar 9, 2023

sprintf_s(command, MAX_PATH_LEN,
This argument to an OS command is derived from , dangerously concatenated into , and then passed to system(_Command).

Starting to think it doesn't know sprintf_s() is the windows "_safe" variants, ie, snprintf().

Copy link
Contributor

@ryao ryao left a comment

Choose a reason for hiding this comment

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

I notice that time_t is 64-bit on 64-bit UNIX systems because long is 64-bit on them, but due to Windows' memory model, long is 32-bit while time_t is 64-bit. The standard has a hole here, since inttypes.h, which should give a way to print this in a sane way, does not. Something like this would probably work:

#ifdef _ILP32
#define PRI_TIME PRId32
#else
#define PRI_TIME PRId64
#endif

(void) printf("%s\t%s\t" PRI_TIME "\n", zname, tagname, time);

We have a similar, but subtly different issue with uid_t, where Linux and presumably other UNIX systems use unsigned int, while Windows appears to use unsigned long long. Interestingly, this reveals a case where the type is not quite right, because %d is used instead of %u.

That said, doing a 64-bit uid_t with the works fine on little endian systems as long as the uid_t being passed is the last argument passed and the top 32-bits are not actually used. If it is not, then the next argument will be considered to start in the upper half of the passed uid_t. On a big endian system, this will pass the top 32-bits of the uid_t, which is wrong, although Windows has not supported big endian in years, so that is less of a concern than the former.

We could solve this similarly to how we can solve the time_t issue, by doing:

#if defined(_ILP32) || !defined(_WIN32)
#define PRI_UID PRIu32
#else
#define PRI_UID PRIu64
#endif
#endif

(void) sprintf(id, PRI_UID, rid);

cp = g->must;
scan = start;
memset(&mbs, 0, sizeof (mbs));
while (cp < g->must + g->mlen) {
Copy link
Contributor

@ryao ryao Mar 9, 2023

Choose a reason for hiding this comment

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

This is a false alarm because memset() is not being used here for security reasons, but it would be nice not to have an alert. While github code scanning will not show this on future PRs after this is merged, it will still appear in the security tab in repositories where we have write access. It is possible to mark it as a false positive there, but it needs to be done for each individual repository and I would rather not have people get into the habit of marking alerts as false positives if it is not necessary. In this case, we could add an #ifdef NLS to suppress the alert from memset().

LOGGER_SESSION, OPEN_ZFS_GUID, flags.c_str(),
levels.c_str(), size_in_mb, etl_file.c_str());

ret = system(command);
Copy link
Contributor

Choose a reason for hiding this comment

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

I had originally looked at this from my ipad and could not see the context due to the iPad being slow, so I made a (now deleted) remark that I now see was wrong.

Anyway, the reason that CodeQL complains here is that command injection is possible by altering the arguments passed to the installer and in general, command injection is something to be avoided whenever possible. Here are some references:

https://codeql.github.com/codeql-query-help/cpp/cpp-command-line-injection/
https://wiki.sei.cmu.edu/confluence/display/c/STR02-C.+Sanitize+data+passed+to+complex+subsystems

That said, I do not know the right way to sanitize this offhand, but the whitelist example by Wietse Venema at the second URL looks like a sane solution.

@lundman lundman force-pushed the windows branch 2 times, most recently from 0019403 to 0efb36e Compare March 9, 2023 23:39
lib/libefi/rdwr_efi_windows.c Outdated Show resolved Hide resolved
unsigned size;
{
(void)opaque;
return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
Copy link
Contributor

Choose a reason for hiding this comment

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

A typecast to (size_t) is needed on size. Otherwise, you can under-allocate memory.

In practice, this probably does not happen at the sizes we allocate, but it is better to be safe than sorry. A patch fixing this is likely needed by upstream zlib, since there is a clear memory safety issue here.

lib/libspl/os/windows/posix.c Outdated Show resolved Hide resolved
include/os/windows/spl/sys/kmem_impl.h Outdated Show resolved Hide resolved
lhp->lh_nchunks = nchunks;
lhp->lh_chunksize = P2ROUNDUP(logsize / nchunks + 1, PAGESIZE);
lhp->lh_base = vmem_alloc_impl(kmem_log_arena,
lhp->lh_chunksize * nchunks, VM_SLEEP);
Copy link
Contributor

Choose a reason for hiding this comment

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

Although we should not be doing allocations that are >=4GB in size, if we ever do, this will underallocate. It should be given a typecast to (size_t) as a defensive measure to harden against future changes that will try to allocate big things.

percent = (used * 100) / kmem_dump_size;

kmem_dumppr(&p, e, "%% heap used,%d\n", percent);
kmem_dumppr(&p, e, "used bytes,%ld\n", used);
Copy link
Contributor

Choose a reason for hiding this comment

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

size_t should change size based on the machine word size, but I am not aware of a printf specifier that does on Windows. We probably will need this in a header somewhere:

#ifdef _ILP32
#define PRI_SIZE "%lu"
#define PRI_SSIZE "%ld"
#else
#define PRI_SIZE "%llu"
#define PRI_SSIZE "%lld"
#endif

Then we could make this:

kmem_dumppr(&p, e, "used bytes," PRI_SIZE "\n", used);

Copy link
Contributor

Choose a reason for hiding this comment

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

This will cause incorrect numbers to be printed whenever bit 32 or greater is used.


kmem_dumppr(&p, e, "%% heap used,%d\n", percent);
kmem_dumppr(&p, e, "used bytes,%ld\n", used);
kmem_dumppr(&p, e, "heap size,%ld\n", kmem_dump_size);
Copy link
Contributor

Choose a reason for hiding this comment

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

The same for this.

include/os/windows/spl/sys/kmem_impl.h Outdated Show resolved Hide resolved
while (reap-- &&
(mp = kmem_depot_alloc(cp, &cp->cache_full)) != NULL) {
kmem_magazine_destroy(cp, mp, cp->cache_magtype->mt_magsize);
bytes += cp->cache_magtype->mt_magsize * cp->cache_bufsize;
Copy link
Contributor

Choose a reason for hiding this comment

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

If the types of these are not widened, we need a defensive (size_t) cast.

if (IS_P2ALIGNED(cache_size, PAGESIZE))
align = PAGESIZE;
(void) snprintf(name, sizeof (name),
"kmem_alloc_%lu", cache_size);
Copy link
Contributor

Choose a reason for hiding this comment

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

Provided that you take my previous suggestion to make PRI_SIZE, we can use that here.

@ryao
Copy link
Contributor

ryao commented Mar 10, 2023

There are many failures in the Linux ZTS tests that need to be diagnosed and fixed. A normal run gives:

Percent passed: 98.4%

The runs being done on this PR give:

Percent passed: 35.3%

@lundman
Copy link
Contributor Author

lundman commented Mar 10, 2023

Percent passed: 35.3%

I will at very least look at the 7

 507234: Memory fault(coredump)

//


./zfs "set"
missing arguments

Segmentation fault

Pfft, cosmetics.

@lundman lundman force-pushed the windows branch 2 times, most recently from 2eaaadf to 0ce5a1c Compare March 10, 2023 08:37
@ryao
Copy link
Contributor

ryao commented Mar 10, 2023

This is interesting. CentOS 7:

Percent passed: 97.0%

CentOS 8:

Percent passed: 36.0%

@lundman lundman force-pushed the windows branch 3 times, most recently from db7af11 to ceb4324 Compare March 11, 2023 06:03
@lundman lundman marked this pull request as ready for review March 11, 2023 12:06
@lundman
Copy link
Contributor Author

lundman commented Mar 18, 2023

Oh yeah, promoted PR, now all grown up.

lundman added 3 commits July 5, 2023 14:38
Signed-off-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Jorgen Lundman <lundman@lundman.net>
For the variabe defined inside kfpu_begin().

Use kfpu_begin_ctx() for the split init/fini functions.

Signed-off-by: Jorgen Lundman <lundman@lundman.net>
@github-advanced-security
Copy link

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

@lundman lundman closed this Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Code Review Needed Ready for review and testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants