Skip to content

Ruby: Add Improper Memoization query #9267

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

Merged
merged 5 commits into from
Jun 17, 2022
Merged

Conversation

hmac
Copy link
Contributor

@hmac hmac commented May 23, 2022

This query finds cases where a method memoizes its result but fails to
include one or more of its parameters in the memoization key (or doesn't
use memoization keys at all). This can lead to the method returning
incorrect results when subsequently called with different arguments.

I've added it to experimental initally, as it can flag methods which aren't security vulnerabilities.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp

errors/warnings:

./ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp:39:14: element "example" not allowed here; expected the element end-tag or element "blockquote", "img", "include", "ol", "p", "pre", "sample", "table", "ul" or "warning"
./ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp:54:58: element "cpde" not allowed anywhere; expected the element end-tag, text or element "a", "b", "code", "em", "i", "img", "strong", "sub", "sup" or "tt"
./ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp:54:76: The element type "cpde" must be terminated by the matching end-tag "</cpde>".
A fatal error occurred: 1 qhelp files could not be processed.

@hmac hmac force-pushed the hmac/improper-memoization branch from 525ffb5 to 9b68e4e Compare May 23, 2022 11:45
@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp

errors/warnings:

./ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp:39:14: element "example" not allowed here; expected the element end-tag or element "blockquote", "img", "include", "ol", "p", "pre", "sample", "table", "ul" or "warning"
./ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp:83:3: The end-tag for element type "example" must end with a '>' delimiter.
A fatal error occurred: 1 qhelp files could not be processed.

@hmac hmac force-pushed the hmac/improper-memoization branch from 9b68e4e to 02a73f0 Compare May 23, 2022 11:47
@github-actions
Copy link
Contributor

github-actions bot commented May 23, 2022

QHelp previews:

ruby/ql/src/experimental/improper-memoization/ImproperMemoization.qhelp

Improper Memoization

A common pattern in Ruby is to memoize the result of a method by storing it in an instance variable. If the method has no parameters, it can simply be stored directly in a variable.

      def answer
        @answer ||= calculate_answer
      end
    

If the method takes parameters, then there are multiple results to store (one for each combination of parameter value). In this case the values should be stored in a hash, keyed by the parameter values.

      def answer(x, y)
        @answer ||= {}
        @answer[x] ||= {}
        @answer[x][y] ||= calculate_answer
      end
    

If a memoization method takes parameters but does not include them in the memoization key, subsequent calls to the method with different parameter values may incorrectly return the same result. This can lead to the method returning stale data, or leaking sensitive information.

Example

In this example, the method does not include its parameters in the memoization key. The first call to this method will cache the result, and subsequent calls will return the same result regardless of what arguments are given.

      def answer(x, y)
        @answer ||= calculate_answer(x, y)
      end
    

This can be fixed by storing the result of calculate_answer in a hash, keyed by the parameter values.

      def answer(x, y)
        @answer ||= {}
        @answer[x] ||= {}
        @answer[x][y] ||= calculate_answer(x, y)
      end
    

Note that if the result of calculate_answer is false or nil, then it will not be cached. To cache these values you can use a different pattern:

      def answer(x, y)
        @answer ||= Hash.new do |h1, x|
          h1[x] = Hash.new do |h2, y|
            h2[y] = calculate_answer(x, y)
          end
        end
        @answer[x][y]
      end
    

@hmac hmac force-pushed the hmac/improper-memoization branch from 02a73f0 to e87eb44 Compare May 23, 2022 11:58
@hmac hmac marked this pull request as ready for review May 24, 2022 11:56
@hmac hmac requested a review from a team as a code owner May 24, 2022 11:56
@hmac hmac force-pushed the hmac/improper-memoization branch from 83f95e4 to fd1e920 Compare May 25, 2022 13:05
Copy link
Contributor

@nickrolfe nickrolfe left a comment

Choose a reason for hiding this comment

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

Looks like a nice query! I have just a few questions and nitpicks.

hmac added 5 commits June 16, 2022 12:44
This query finds cases where a method memoizes its result but fails to
include one or more of its parameters in the memoization key (or doesn't
use memoization keys at all). This can lead to the method returning
incorrect results when subsequently called with different arguments.
@hmac hmac force-pushed the hmac/improper-memoization branch from 5b328e7 to 3112964 Compare June 16, 2022 00:44
Copy link
Contributor

@nickrolfe nickrolfe left a comment

Choose a reason for hiding this comment

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

👍🚀

@hmac hmac merged commit 230192d into github:main Jun 17, 2022
@hmac hmac deleted the hmac/improper-memoization branch June 17, 2022 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants