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

Added sanity checks before allocating memory after reads #3765

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

0sidharth
Copy link

@0sidharth 0sidharth commented Mar 19, 2020

Aims to fix #3606 . Went through the 273 instances and added sanity checks. Many of these instances already had sanity checks and all of them vary a lot, so I have adapted what I thought would be optimal for the ones that did not have any checks. It's my first PR in PCL, so please point out any miscellaneous mistakes too.

@SergioRAgostinho SergioRAgostinho added changelog: enhancement Meta-information for changelog generation needs: code review Specify why not closed/merged yet labels Mar 19, 2020
Copy link
Member

@kunaltyagi kunaltyagi left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @0sidharth

This is just a partial review, since I feel a lot of the code needs minor tweaking.

EDIT: Do check the errors on the CI

common/include/pcl/common/impl/bivariate_polynomial.hpp Outdated Show resolved Hide resolved
recognition/src/dotmod.cpp Outdated Show resolved Hide resolved
recognition/src/dotmod.cpp Outdated Show resolved Hide resolved
@larshg
Copy link
Contributor

larshg commented Mar 19, 2020

I looked quickly at the style guide, but there are no mentioning of how sanity checks should generally be implemented.

but I would suggest in stead of the following:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename real> void
pcl::BivariatePolynomialT<real>::readBinary (std::istream& os)
{
  memoryCleanUp ();
  os.read (reinterpret_cast<char*> (&this->degree), sizeof (int));
  unsigned int paramCnt = getNoOfParametersFromDegree (this->degree);
  parameters = new real[paramCnt];
  os.read (reinterpret_cast<char*> (&(*this->parameters)), paramCnt * sizeof (real));
  if (os.bad())
  {
    PCL_THROW_EXCEPTION (pcl::IOException, "Failure in reading degree from file");
  }
  else if (os.fail())
  {
    PCL_THROW_EXCEPTION (pcl::IOException, "failbit set while reading degree (formatting or extraction error");
  }
  else
  {
    unsigned int paramCnt = getNoOfParametersFromDegree (this->degree);
    parameters = new real[paramCnt];
    os.read (reinterpret_cast<char*> (&(*this->parameters)), paramCnt * sizeof (real));
  }
}

write it like this:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename real> void
pcl::BivariatePolynomialT<real>::readBinary (std::istream& os)
{
  memoryCleanUp ();
  os.read (reinterpret_cast<char*> (&this->degree), sizeof (int));
  
  // Missing a check here on degree?
 
  unsigned int paramCnt = getNoOfParametersFromDegree (this->degree);
  parameters = new real[paramCnt];
  os.read (reinterpret_cast<char*> (&(*this->parameters)), paramCnt * sizeof (real));
  
  if (os.bad())
  {
    PCL_THROW_EXCEPTION (pcl::IOException, "Failure in reading degree from file");
  }
  
  if (os.fail())
  {
    PCL_THROW_EXCEPTION (pcl::IOException, "failbit set while reading degree (formatting or extraction error");
  }

  unsigned int paramCnt = getNoOfParametersFromDegree (this->degree);
  parameters = new real[paramCnt];
  os.read (reinterpret_cast<char*> (&(*this->parameters)), paramCnt * sizeof (real));
}

I'm sure if there are any advantages writing two ifs after each other over, if-else - but I guess the two errors are not necessarily dependent on each other or related, so I think it gives better clarity that they are seperated. It is ofc. the same output input stream that are checked. Got fooled by the parameter name (os).

Genereally I don't think we need the last else clauses. If one of the errors are true, an exception is thrown. If not, we can continue execution the following code.

but in the end its a "style" and I just skimmed it quickly, but generally I think it looks good.
But wait for others to chime in before going ahead and changing the code. I'm not a maintainer, just wanted to give my thoughts.

@larshg
Copy link
Contributor

larshg commented Mar 19, 2020

@kunaltyagi would it make sense to check (not sure what can be checked for though) the istream object in top of the method - before reading anything?

@larshg
Copy link
Contributor

larshg commented Mar 19, 2020

Just quickly looked istream - maybe use good instead? EDIT: It also checks if EOF was reached, so not sure if it would be set, if it is a last variable that is read or it is only set if you try to read beyond the file.

Edit2: Its also possible to use ! - which is synonym for fail, which checks both fail and bad bit.

Depends if we want detailed description if it was either bad or fail - whatever the differences can be here.

@0sidharth
Copy link
Author

@larshg I had considered good(), however I was a bit confused as while going through all the files that have an instance of read, they have largely varying sanity checks, varying in what they are checking and how they are indicating a fault, so I just went with what felt good to me. Also I am checking badbit before failbit so I dont think two ifs would make much of a difference, wouldn't mind making the changes though if a maintainer feels they are better.

@0sidharth
Copy link
Author

@kunaltyagi I looked over the CI errors, but couldn't find errors related to my code or specific lines where formatting fails. As for build, it is building on my Ubuntu 16.04, I don't understand why it is failing on 19.10 from the error message. Could you or any other maintainers guide me on what to do regarding these?

@larshg
Copy link
Contributor

larshg commented Mar 19, 2020

@0sidharth Okay :) Well, its nice to get it more unified then!
Might be a good idea to add it to the style section as well, whatever solution get chosen.

@kunaltyagi
Copy link
Member

For the CI:

  • Run the format target to get format CI working
  • Ignore the 19.10 for a little while
  • I've rerun Windows CI, maybe the test failure was a fluke, maybe some one merged something wrong on master

use good instead

Using good wouldn't be a way forward. It checks if the next read will succeed. Using ! is a possibility. For reads, bad can only happen for first read, if it succeeds, all others will fail(). So maybe we can use bad after the first read and ! for the rest. This is for read only, and not write

would it make sense to check ... the istream object ... before reading anything?

The incoming stream is assumed to be working, just like elsewhere in PCL (eg: ostream<< functions), so no need to check at the top of the function. Though it is good defensive-coding strategy, it doesn't fit in with existing code.

@SergioRAgostinho SergioRAgostinho added needs: more work Specify why not closed/merged yet and removed needs: code review Specify why not closed/merged yet labels Mar 20, 2020
@taketwo
Copy link
Member

taketwo commented Mar 20, 2020

if (os.bad())
{
  PCL_THROW_EXCEPTION (pcl::IOException, "Failure in reading degree from file");
}
  
if (os.fail())
{
  PCL_THROW_EXCEPTION (pcl::IOException, "failbit set while reading degree (formatting or extraction error");
}

If we are going to repeat this in many places, it's maybe worth adding a specialized macro. Something like PCL_CHECK_IO_STREAM, that checks both bad and fail, and bails out with IOException with an appropriate message.

PCL_CHECK_IO_STREAM(os, "degree")

@0sidharth
Copy link
Author

@taketwo Currently I have implemented a function to do the same, should I change it to a macro?

@SergioRAgostinho
Copy link
Member

Hmm. I think I do prefer a function rather than a macro. But it don't think it needs to be a static function of IOException.

@SergioRAgostinho SergioRAgostinho added the needs: feedback Specify why not closed/merged yet label Mar 21, 2020
@taketwo
Copy link
Member

taketwo commented Mar 21, 2020

PCL_THROW_EXCEPTION automatically includes file name, code line, and function name in the exception. This information is helpful for debugging. If we move these throws inside a helper function, as you proposed, then this information will not reflect the actual calling site, but rather the helper function.

I know, macros are "bad", but I think in this case they are justified. I'm absolutely open to other suggestions, though.

@SergioRAgostinho
Copy link
Member

If we move these throws inside a helper function, as you proposed, then this information will not reflect the actual calling site, but rather the helper function.

In light of this I yield my original position in favor of the macro.

@0sidharth
Copy link
Author

0sidharth commented Mar 21, 2020

Alright, I have changed it from a function to a macro, please review it.

Copy link
Member

@kunaltyagi kunaltyagi left a comment

Choose a reason for hiding this comment

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

Partially reviewed, but some points mentioned are present in multiple of the files touched.

common/include/pcl/exceptions.h Outdated Show resolved Hide resolved
features/src/narf.cpp Outdated Show resolved Hide resolved
io/src/ifs_io.cpp Outdated Show resolved Hide resolved
io/src/ifs_io.cpp Outdated Show resolved Hide resolved
io/src/ifs_io.cpp Outdated Show resolved Hide resolved
io/src/ifs_io.cpp Outdated Show resolved Hide resolved
@SergioRAgostinho
Copy link
Member

@0sidharth don't force push until the review is over. If you force push GitHub is no longer able to show reviewers only the subset of changes which were added and we need to check everything again. Not very time efficient.

You should only force push after the reviews are complete, if we ask you to squash commits or rebase with master.

@kunaltyagi
Copy link
Member

Changeset looks good, structurally. Only a few topical issues need to be addressed.

kunaltyagi
kunaltyagi previously approved these changes Mar 25, 2020
Copy link
Member

@kunaltyagi kunaltyagi left a comment

Choose a reason for hiding this comment

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

LGTM. Waiting on sergio

@kunaltyagi kunaltyagi added needs: code review Specify why not closed/merged yet and removed needs: more work Specify why not closed/merged yet needs: feedback Specify why not closed/merged yet labels Mar 25, 2020
@0sidharth
Copy link
Author

@SergioRAgostinho

@SergioRAgostinho
Copy link
Member

Other stuff to have a look first. You'll need to wait.

Copy link
Member

@SergioRAgostinho SergioRAgostinho left a comment

Choose a reason for hiding this comment

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

Overall LGTM. We just need to address this new type of Exception which I believe to be necessary when some of the parsed values don't make sense.


if (point_diff_color_data_vector_size < 0)
{
PCL_THROW_EXCEPTION (pcl::IOException, "Error! Size of pointDiffColorDataVector specified in the file is negative!");
Copy link
Member

@SergioRAgostinho SergioRAgostinho Apr 3, 2020

Choose a reason for hiding this comment

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

This exception is complaining about a bad/incorrect numerical value. It's not really an IO issue anymore. This is the wrong type of exception to throw in this situation, but unfortunately I don't see an existing PCL Exception which matches what we would likely want to express here. Maybe BadArgumentException? Otherwise I'm open to create a new exception type and I'm looking for suggestions on a name which better expresses what is happening here.

Copy link
Author

Choose a reason for hiding this comment

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

I don't think BadArgumentException is suited for this situation. Perhaps a new exception BadValueInFileException?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe just BadValue so that it can be used in other contexts.

ml/include/pcl/ml/dt/decision_forest.h Outdated Show resolved Hide resolved
recognition/include/pcl/recognition/quantized_map.h Outdated Show resolved Hide resolved
recognition/src/dotmod.cpp Outdated Show resolved Hide resolved
@kunaltyagi kunaltyagi added needs: more work Specify why not closed/merged yet and removed needs: code review Specify why not closed/merged yet labels Apr 3, 2020
@kunaltyagi
Copy link
Member

Should we add multiple modules in the label for PR like this?

@SergioRAgostinho
Copy link
Member

I believe the usual policy is to not add anything if it touches a lot of modules. If they're low in number feel free to.

@SergioRAgostinho SergioRAgostinho added the needs: feedback Specify why not closed/merged yet label Apr 4, 2020
Comment on lines +120 to +122
for (auto sub_node_index : sub_nodes)
{
sub_nodes[sub_node_index].serialize (stream);
sub_node_index.serialize (stream);
Copy link
Member

Choose a reason for hiding this comment

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

Use auto& or const auto& depending on context (L120, L165). There's no need for making extra copies.

@kunaltyagi
Copy link
Member

Pinging for updates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog: enhancement Meta-information for changelog generation needs: feedback Specify why not closed/merged yet needs: more work Specify why not closed/merged yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Check if read succedded before reading again/allocating memory
5 participants