Skip to content

vss: fix potential crash (not reachable in restic)#3045

Merged
MichaelEischer merged 6 commits intorestic:masterfrom
fgma:vss-potential-crash
Nov 4, 2020
Merged

vss: fix potential crash (not reachable in restic)#3045
MichaelEischer merged 6 commits intorestic:masterfrom
fgma:vss-potential-crash

Conversation

@fgma
Copy link
Copy Markdown
Contributor

@fgma fgma commented Oct 29, 2020

What does this PR change? What problem does it solve?

This fixes a potential crash in the VSS code. This is currently not reachable because restic checks for sufficient permissions before calling this code. If used somewhere this ends up dereferencing nil.

Was the change discussed in an issue or in the forum before?

Mentioned in #2274 by @tigerwill90

Checklist

  • I have read the Contribution Guidelines
  • I have enabled maintainer edits for this PR
  • I have added tests for all changes in this PR
  • I have added documentation for the changes (in the manual)
  • There's a new file in changelog/unreleased/ that describes the changes for our users (template here)
  • I have run gofmt on the code in all commits
  • All commit messages are formatted in the same style as the other commits in the repo
  • I'm done, this Pull Request is ready for review

@greatroar
Copy link
Copy Markdown
Contributor

Wouldn't it be cleaner to return an error from initializeVssCOMInterface when it gets a nil? Just asking, I've no idea what the effect on HasSufficientPrivilegesForVSS would be.

@tigerwill90
Copy link
Copy Markdown

tigerwill90 commented Oct 29, 2020

Wouldn't it be cleaner to return an error from initializeVssCOMInterface when it gets a nil? Just asking, I've no idea what the effect on HasSufficientPrivilegesForVSS would be.

Actually the HRESULT code is correct and fall back to E_ACCESSDENIED case before crashing. It seems possible to treat all result code and error case in initializeVssCOMInterface() and returning only oleIUnknown or an error but you finally have to type check errors to keep actual behaviour of HasSufficientPrivilegesForVSS().

@greatroar
Copy link
Copy Markdown
Contributor

I'm not sure what you mean. Is there a case where oleIUnknown is nil but still usable? I.e., is the following incorrect?

// initializeCOMInterface initialize an instance of the VSS COM api
func initializeVssCOMInterface() (*ole.IUnknown, uintptr, error) {
	vssInstance, err := loadIVssBackupComponentsConstructor()
	if err != nil {
		return nil, 0, err
	}

	// ensure COM is initialized before use
	ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)

	var oleIUnknown *ole.IUnknown
	result, _, _ := vssInstance.Call(uintptr(unsafe.Pointer(&oleIUnknown)))
	if oleIUnknown == nil {
		return nil, 0, errors.Errorf("VSS: got nil OLE object") // or whatever
	}

	return oleIUnknown, result, nil
}

@DRON-666
Copy link
Copy Markdown
Contributor

Ideally HRESULT should be returned only as part of vssError (functions like Cancel, Wait, QueryStatus must be changed) and all function such as DoSnapshotSet, GatherWriterMetadata, PrepareForBackup, BackupComplete, initializeVssCOMInterface, queryInterface should call Release itself and return nil, err. Also situation where returned COMinterface!=nil and err==nil or COMinterface==nil and err!=nil should be impossible.

@greatroar
Copy link
Copy Markdown
Contributor

@DRON-666 PR?

@DRON-666
Copy link
Copy Markdown
Contributor

Yes @greatroar, I should write PR, but problem is that my developer skills predates git, github and golang. I want to contribute to this project, but definitely not today.

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Oct 30, 2020

I've cleaned it up. It now handles errors in initializeVssCOMInterface() properly. I've tested it by removing the call to HasSufficientPrivilegesForVSS(). This leads to a to expected error:

VSS error: Failed to initialize COM interface: E_ACCESSDENIED (0x80070005)

@DRON-666
Copy link
Copy Markdown
Contributor

I've cleaned it up.

After that switch HRESULT(result) cases become mostly unreachable.

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Oct 31, 2020

@DRON-666 To make it clean we should also move the error handling in the switch statement into initializeVssCOMInterface(). I'd like to keep the special error handling telling the user about necessary privileges or being an administrator for easy troubleshooting.

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Nov 1, 2020

I've move error handling to initializeVssCOMInterface(). It will still return a the result because it is needed in HasSufficientPrivilegesForVSS().

@rawtaz
Copy link
Copy Markdown
Contributor

rawtaz commented Nov 3, 2020

Is there much left to do on this or is it ready for merging soon?

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Nov 3, 2020

I'm right now thinking about @DRON-666's remarks.

@fd0
Copy link
Copy Markdown
Member

fd0 commented Nov 3, 2020

Let us know when you're done :)

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Nov 3, 2020

Just waiting for @DRON-666 approve my changes.

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Nov 3, 2020

I also thought about it but I can't easily get around it. Its essentially an implementation detail. In case multiple other things might fail before permissions are checked the API might return a different error. All other possible errors won't give any information about permissions. Actually checking for correct permissions is a bit more involved on windows. I implemented this in #2875 which is still open for discussion.

@MichaelEischer
Copy link
Copy Markdown
Member

I also thought about it but I can't easily get around it. Its essentially an implementation detail. In case multiple other things might fail before permissions are checked the API might return a different error. All other possible errors won't give any information about permissions. Actually checking for correct permissions is a bit more involved on windows. I implemented this in #2875 which is still open for discussion.

The cleanest fix would be to let HasSufficientPrivilegesForVSS return true/false/"something else went wrong". That is return (bool, err). That way the initial permissions check could already return allowed/denied/some other error to the user.

@greatroar
Copy link
Copy Markdown
Contributor

greatroar commented Nov 4, 2020

The only call site turns a false result into a fatal error anyway, so just returning the error would be enough :)

@DRON-666
Copy link
Copy Markdown
Contributor

DRON-666 commented Nov 4, 2020

If err != nil but of a different type, the function returns true. That doesn't seem right.

Yes and code from cmd_backup.go should be

if err := fs.HasSufficientPrivilegesForVSS(); err != nil {
    return err
}

Now it's convert HRESULT to error, then to bool and then to different error.

@fgma
Copy link
Copy Markdown
Contributor Author

fgma commented Nov 4, 2020

HasSufficientPrivilegesForVSS() now returns only an error

@rawtaz
Copy link
Copy Markdown
Contributor

rawtaz commented Nov 4, 2020

@DRON-666 and @greatroar Does your thumbs-up clickety-clicks mean that you have no further comments on this PR, that you think it's fine to merge?

Copy link
Copy Markdown
Member

@MichaelEischer MichaelEischer left a comment

Choose a reason for hiding this comment

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

LGTM.

@MichaelEischer MichaelEischer merged commit 916b2d3 into restic:master Nov 4, 2020
mfrischknecht pushed a commit to mfrischknecht/restic that referenced this pull request Jun 14, 2022
HasSufficientPrivilegesForVSS() now returns an error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants