Skip to content

stdlib audit: inheritable DLS for configurable options#11228

Merged
Octachron merged 2 commits intoocaml:trunkfrom
Octachron:domain_local_default_option
May 3, 2022
Merged

stdlib audit: inheritable DLS for configurable options#11228
Octachron merged 2 commits intoocaml:trunkfrom
Octachron:domain_local_default_option

Conversation

@Octachron
Copy link
Copy Markdown
Member

The Hashtbl and Filename modules expose two configurable default options as global mutable states: the state in Hashtbl tracks if hash tables are randomized by default, whereas the one in Filename stores the default directory name for temporary files.

The expected use case for both options is that they will be set during the initialization part of the program and then left alone.

This PR proposes to make this policy more explicit by storing those configurable options as domain-local states which are inherited by children domains. With this change, the behavior of concurrent mutation of those state is more specified and it is no longer possible to use those global variables for synchronization purpose.

Another option would be to document the expected policy for updating those variables in #11227.

@gasche
Copy link
Copy Markdown
Member

gasche commented Apr 29, 2022

When I call Hashtbl.randomize (), I believe that I want the guarantee that all further hash-table creations will be randomized. With the proposed PR, I get a weaker guarantee: all further hash-table creations on the current domain and its transitive children will be randomized. This is equivalent if the call happens before spawning any domain, but not in the general case of calling the function from "some" domain. I'm a bit nervous about this inequivalence.

We could consider the following:

  • Keep a single configuration state, using an atomic boolean variable. I think this option has my preference: in theory domain-local storage avoids synchronization costs for reading this option at hashtable-creation time, but in fact I would expect that reading from an atomic boolean is fast enough, and the domain-local code is significantly more complex, at least conceptually.
  • Forbid setting the flag once several domains have been spawned, and fail with an error. Meh.
  • Add an option to the DLS interface to mutate a value for all domains where the corresponding key is defined. This could be difficult to implement (does it require a stop-the-world? meh) and it may be a dangerous option to offer to the user.

So I guess my question is: do we want domain-local state here or really global (atomic) state?

@xavierleroy
Copy link
Copy Markdown
Contributor

Like @gasche I would prefer the setting for hash table randomization to be an atomic global rather than per-domain. A per-domain setting for the temporary directory makes perfect sense, but the hash table randomization setting is peculiar, in particular because it (purposefully) can be changed only once.

Applications that want to protect against hash table collision attacks are supposed to call Hashtbl.randomize() before any hash table is created. If domains have already been spawned when Hashtbl.randomize() is called, something weird is happening but it can still be secure if these domains haven't created any hash table yet. For this scenario to work, the setting must be an atomic global. An inherited per-domain setting is less secure.

@Octachron
Copy link
Copy Markdown
Member Author

I agree that it also make sense to have randomized a global setting. However, I am not sure if it should be an atomic itself?

If multiple domains are at play, don't we have the problem that happen-after is no longer a total order? Restoring a order before setting randomized and other threads requires a synchronization mechanism that must be external to the randomized setting: For a domain waiting on randomized to change value to true can be more efficiently done by setting randomized to true on the domain.

Thus I have the impression that changing randomized to atomic would only help in the following scenario

Domain 1 Domain 2
1. Hashtbl.randomize () 3. log "before Hashtbl creation"
2. log "Hastbl randomized" 4. let h = Hashtbl.create 10
5. log "after Hashtbl"

by ensuring that the order 2 - 3 - 4 - 1 - 5 cannot happen even if the log operation is not atomic. But do we want to offer such synchronization guaranteed on the randomized status?

@xavierleroy
Copy link
Copy Markdown
Contributor

You're probably right that an atomic bool or a bool ref make little difference in this particular case. The good thing about atomic bool is that it makes the intent clear right in the source code: yes, this is not protected by a mutex, but we have determined that it's OK to access concurrently.

@Octachron
Copy link
Copy Markdown
Member Author

I also found a counter-example to my mental model of the issue:

Domain 1 Domain 2
Hashtbl.randomize () while true do let h = Hashtbl.create 10 in () done

I would expect that the second domain eventually see the write to randomized but this property only holds with an atomic.

@Octachron
Copy link
Copy Markdown
Member Author

I have switched to an atomic for randomized.

Copy link
Copy Markdown
Contributor

@xavierleroy xavierleroy left a comment

Choose a reason for hiding this comment

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

Looks very good to me. Minor documentation nitpick below.

protect themselves against the denial-of-service attack described
in {!create} call [Hashtbl.randomize()] at initialization
time.
time before any children domains are created.
Copy link
Copy Markdown
Contributor

@xavierleroy xavierleroy May 2, 2022

Choose a reason for hiding this comment

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

I believe "child domains" is more widely used than "children domains". An example from an Oracle documentation: "When a UNIX process initiates one or more dependent processes, we call these child processes, or children."

This said you could also write "before any domains are created".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I believe that child domains is better than any domains since there is no ambiguity on the first domain. And yes child domains sound better than children domains.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But doesn't child introduce a concept that doesn't really exist ? AFAIK there no notion of parent/child relationship in domains.

Yet another proposal: before any new domain is created.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There is a notion of parent and child domains introduced by domain local storage. Nevertheless, you are right that it better to not introduce adjacent concepts in the description. At the same time, speaking of before new domains muddles a bit the notion of before. So maybe before any domains is the right compromise since the main domain cannot be created by users.

@Octachron Octachron force-pushed the domain_local_default_option branch 2 times, most recently from 3e66caf to 13057dd Compare May 2, 2022 13:43
(** Change the temporary directory returned by {!Filename.get_temp_dir_name}
and used by {!Filename.temp_file} and {!Filename.open_temp_file}.
The temporary directory is a domain-local value which is inherited
by children domains.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Another "children domains" that I missed on first reading.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed, there are no longer any remnant of "children domains" in the repository.

@Octachron Octachron force-pushed the domain_local_default_option branch from 13057dd to 8216acb Compare May 3, 2022 08:54
@Octachron Octachron merged commit 7a02db4 into ocaml:trunk May 3, 2022
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.

4 participants