Skip to content

client: do not proceed with I/O if filehandle is invalid#55457

Merged
vshankar merged 4 commits intoceph:mainfrom
dparmar18:wip-64313
Mar 6, 2024
Merged

client: do not proceed with I/O if filehandle is invalid#55457
vshankar merged 4 commits intoceph:mainfrom
dparmar18:wip-64313

Conversation

@dparmar18
Copy link
Contributor

@dparmar18 dparmar18 commented Feb 6, 2024

Contribution Guidelines

  • To sign and title your commits, please refer to Submitting Patches to Ceph.

  • If you are submitting a fix for a stable branch (e.g. "quincy"), please refer to Submitting Patches to Ceph - Backports for the proper workflow.

  • When filling out the below checklist, you may click boxes directly in the GitHub web UI. When entering or editing the entire PR message in the GitHub web UI editor, you may also select a checklist item by adding an x between the brackets: [x]. Spaces and capitalization matter when checking off items this way.

Checklist

  • Tracker (select at least one)
    • References tracker ticket
    • Very recent bug; references commit where it was introduced
    • New feature (ticket optional)
    • Doc update (no ticket needed)
    • Code cleanup (no ticket needed)
  • Component impact
    • Affects Dashboard, opened tracker ticket
    • Affects Orchestrator, opened tracker ticket
    • No impact that needs to be tracked
  • Documentation (select at least one)
    • Updates relevant documentation
    • No doc update is appropriate
  • Tests (select at least one)
Show available Jenkins commands
  • jenkins retest this please
  • jenkins test classic perf
  • jenkins test crimson perf
  • jenkins test signed
  • jenkins test make check
  • jenkins test make check arm64
  • jenkins test submodules
  • jenkins test dashboard
  • jenkins test dashboard cephadm
  • jenkins test api
  • jenkins test docs
  • jenkins render docs
  • jenkins test ceph-volume all
  • jenkins test ceph-volume tox
  • jenkins test windows
  • jenkins test rook e2e

@dparmar18 dparmar18 force-pushed the wip-64313 branch 2 times, most recently from f6da755 to 188c5d3 Compare February 9, 2024 13:26
@dparmar18
Copy link
Contributor Author

last push made some minor naming corrections

@mchangir
Copy link
Contributor

Why are low level interface methods being exercised rather than the usual high level methods ?

@dparmar18
Copy link
Contributor Author

Why are low level interface methods being exercised rather than the usual high level methods ?

Client::_preadv_pwritev_locked is called by I/O APIs, better to fix it here rather than doing the same everywhere

@mchangir
Copy link
Contributor

Why are low level interface methods being exercised rather than the usual high level methods ?

Client::_preadv_pwritev_locked is called by I/O APIs, better to fix it here rather than doing the same everywhere

I meant to ask "Why are you explicitly invoking the low-level APIs directly in your test rather then invoking the high level entry points?"

@dparmar18
Copy link
Contributor Author

Why are low level interface methods being exercised rather than the usual high level methods ?

Client::_preadv_pwritev_locked is called by I/O APIs, better to fix it here rather than doing the same everywhere

I meant to ask "Why are you explicitly invoking the low-level APIs directly in your test rather then invoking the high level entry points?"

ah right, so it's the way nonblocking.cc is being used. it is intended to test ll_preadv_pwritev thoroughly. (atleast that is what im following looking at the existing test case)

@vshankar vshankar requested a review from a team February 14, 2024 05:25
{
ceph_assert(ceph_mutex_is_locked_by_me(client_lock));

if (fh == NULL || !fh_exists(fh)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you checked other unchecked uses to the file handles?

Copy link
Contributor

Choose a reason for hiding this comment

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

... and this isn't really async specific, isn't it? (given that the test case is in nonblocking.cc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... and this isn't really async specific, isn't it? (given that the test case is in nonblocking.cc).

right, not specific to async

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have you checked other unchecked uses to the file handles?

I found most cases handled in respective APIs, this seems to be the only condition(so far) that wasn't checked. If you can think of any/find any do let me know.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is it possible that a file handle has no inode associated with it?

Copy link
Contributor

Choose a reason for hiding this comment

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

... and this isn't really async specific, isn't it? (given that the test case is in nonblocking.cc).

right, not specific to async

Move the test to generic location then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can goto src/test/libcephfs/test.cc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have two kind of APIs:

  1. that accept fd (an int) as an arg
  2. that accept pointer to file handle as an arg. (sync + async)

Most APIs fall in 1st case and all of them have a check for file handle pointer's validness:

Fh *fh = get_filehandle(fd);
    if (!fh)
      return -CEPHFS_EBADF;

we already have tests for this in test.cc (test case named BadFileDesc). So the sync calls are sorted.

It is only the 2nd case where APIs directly make use of the file handle pointers and there are only three of them i.e. ll_preadv_pwritev, ll_writev and ll_readv.

My current implementation adds the check to helper func _preadv_pwritev_locked that all three of these use but the APIs that fall in 1st case make sure they check for it before passing in the fh to the helpers. So my question is what should be done, should I add these checks in all the three APIs (which i feel looks redundant) or keep it the way it is i.e. have the helper check for it? @mchangir @vshankar

Copy link
Contributor

Choose a reason for hiding this comment

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

My current implementation adds the check to helper func _preadv_pwritev_locked that all three of these use but the APIs that fall in 1st case make sure they check for it before passing in the fh to the helpers. So my question is what should be done, should I add these checks in all the three APIs (which i feel looks redundant) or keep it the way it is i.e. have the helper check for it? @mchangir @vshankar

Just have the fd checked in _preadv_pwritev_locked.

Copy link
Contributor Author

@dparmar18 dparmar18 Feb 20, 2024

Choose a reason for hiding this comment

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

this breaks up higher level APIs, eventually every API calls Client::_preadv_pwritev_locked but the thing is high level APIs add fh to the fd_map while low level APIs make use of ll_unclosed_fh_set so if the file is opened using high level API like open or openat then those calls will fails since it wont be able to find the fh in ll_unclosed_fh_set as it was never added to it(the helper I added looks in ll_unclosed_fh_set)

@github-actions
Copy link

This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved

@dparmar18
Copy link
Contributor Author

dparmar18 commented Feb 17, 2024

a fh pointer is stored in ll_unclosed_fh_set only if a file was opened using Client::ll_open or Client::ll_create. This is fine but let's think of a case where the client opens a file with the high level APIs like Client::open or Client::openat (which makes uses of Client::create_and_open which stores the fh pointer in fd_map); fetches the file handle and uses the retrieved pointer to feed into the low level API like Client::ll_writev and since ll_unclosed_fh_set has no fh in it(because file wasn't opened with low level APIs), we have a trouble here. Does this case(of using high level and low level APIs together) hold any ground @vshankar @mchangir ?

@vshankar
Copy link
Contributor

a fh pointer is stored in ll_unclosed_fh_set only if a file was opened using Client::ll_open or Client::ll_create. This is fine but let's think of a case where the client opens a file with the high level APIs like Client::open or Client::openat (which makes uses of Client::create_and_open which stores the fh pointer in fd_map); fetches the file handle and uses the retrieved pointer to feed into the low level API like Client::ll_writev and since ll_unclosed_fh_set has no fh in it(because file wasn't opened with low level APIs), we have a trouble here. Does this case(of using high level and low level APIs together) hold any ground @vshankar @mchangir ?

Are you mixing using the high and low level calls? If yes, that need to be avoided - you either use the high level or the low level, isn't it @dparmar18 ?

@vshankar
Copy link
Contributor

a fh pointer is stored in ll_unclosed_fh_set only if a file was opened using Client::ll_open or Client::ll_create. This is fine but let's think of a case where the client opens a file with the high level APIs like Client::open or Client::openat (which makes uses of Client::create_and_open which stores the fh pointer in fd_map); fetches the file handle and uses the retrieved pointer to feed into the low level API like Client::ll_writev and since ll_unclosed_fh_set has no fh in it(because file wasn't opened with low level APIs), we have a trouble here. Does this case(of using high level and low level APIs together) hold any ground @vshankar @mchangir ?

Are you mixing using the high and low level calls? If yes, that need to be avoided - you either use the high level or the low level, isn't it @dparmar18 ?

Ethically yes, but if someone decides to play around with it, the above case prevails. Theoretically we should factor this in, no?

Is someone is shooting themselves in the foot, we can only do so much, isn't it?

@vshankar
Copy link
Contributor

@dparmar18 I'm now totally confused as what this change is aiming to fix - it was much clear earlier, sorry!

@dparmar18
Copy link
Contributor Author

a fh pointer is stored in ll_unclosed_fh_set only if a file was opened using Client::ll_open or Client::ll_create. This is fine but let's think of a case where the client opens a file with the high level APIs like Client::open or Client::openat (which makes uses of Client::create_and_open which stores the fh pointer in fd_map); fetches the file handle and uses the retrieved pointer to feed into the low level API like Client::ll_writev and since ll_unclosed_fh_set has no fh in it(because file wasn't opened with low level APIs), we have a trouble here. Does this case(of using high level and low level APIs together) hold any ground @vshankar @mchangir ?

Are you mixing using the high and low level calls? If yes, that need to be avoided - you either use the high level or the low level, isn't it @dparmar18 ?

Ethically yes, but if someone decides to play around with it, the above case prevails. Theoretically we should factor this in, no?

Is someone is shooting themselves in the foot, we can only do so much, isn't it?

Well at least they can be warned, but if this doesn't make sense then I'll switch to calling the helper from the low level APIs since this helper can't be used in Client::_preadv_pwritev_locked because it is used by both high and low level APIs

@dparmar18
Copy link
Contributor Author

@dparmar18 I'm now totally confused as what this change is aiming to fix - it was much clear earlier, sorry!

yeah since it is incomplete, it is bound to create confusions, wait a bit im pushing the changes since i know the answer for #55457 (comment)

It is named _ll_fh_exists meaning it is a helper func
only to be used with low level APIs.

Fixes: https://tracker.ceph.com/issues/64313
Signed-off-by: Dhairya Parmar <dparmar@redhat.com>
@dparmar18 dparmar18 force-pushed the wip-64313 branch 2 times, most recently from 24c962a to 6a23327 Compare February 20, 2024 12:19
Along with some minor adjustments to the code to make use of
same int for all the ops.

Fixes: https://tracker.ceph.com/issues/64313
Signed-off-by: Dhairya Parmar <dparmar@redhat.com>
and guard the `if (!mref_reader.is_state_satisfied())` stmt with braces.

Fixes: https://tracker.ceph.com/issues/64313
Signed-off-by: Dhairya Parmar <dparmar@redhat.com>
@dparmar18
Copy link
Contributor Author

[----------] 25 tests from TestClient (7927 ms total)

[----------] Global test environment tear-down
[==========] 25 tests from 1 test suite ran. (7927 ms total)
[  PASSED  ] 25 tests.

@dparmar18
Copy link
Contributor Author

jenkins test api

@vshankar
Copy link
Contributor

@vshankar
Copy link
Contributor

vshankar added a commit to vshankar/ceph that referenced this pull request Mar 4, 2024
* refs/pull/55457/head:
	client: check for bad file handle in low level I/O APIs
	client: check for bad file handle in ll_preadv_pwritev
	client: add function to check if file handle exists
	src/test: test async I/O with invalid/closed file handle
@vshankar
Copy link
Contributor

vshankar commented Mar 4, 2024

Copy link
Contributor

@vshankar vshankar left a comment

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants