Planning to move the CWD to the last position in the code path for OTP-29

Currently, the Current Working Directory (CWD) is automatically added to the front of the code path for looking up and loading modules. While convenient, this default behavior introduces two primary concerns:

1. Security Risk (Module Shadowing)

The CWD’s position at the front of the code path is a potential security issue. If an application is started in a compromised directory, any local .beam files could take precedence over the actual modules of your application.

2. Developer Confusion

This behavior can also be a source of confusion during development. If you happen to compile a module directly into the CWD, but your build system later places the official .beam files elsewhere, the CWD version will be loaded. This often results in subtle, hard-to-debug errors where you are inadvertently loading the wrong version of a module.

The Trade-off

On the other hand, we acknowledge that having the CWD included by default is highly convenient for quick experiments and local development workflows.

There may be existing tutorials on the web that rely on this behavior.

Therefore, for OTP-29, we plan to move the CWD to the end of the code path instead of removing it entirely.

If this change breaks your existing build or startup scripts, you can explicitly add the CWD back to the front of the code path using the standard -pa . flag.

TL;DR

We’re planning to move the CWD to the last position in the code path for OTP-29.

We want to hear your opinions and experiences regarding this change:

  1. What is your opinion on moving the CWD to the last position in the code path for OTP-29? (The plan)

  2. What are your thoughts on a full removal of the CWD from the default code path in a future release?

  3. Do you have any existing tools or workflows that rely on the CWD being in (or at the front of) the code path?

5 Likes
  1. Not a problem.
  2. That would make life worse for developers – we’ve had decades to get used to and assume the current behaviour. If you need this for security add an opt-in option that production deployments can use.
  3. No.
1 Like

Yes, it’s how we manage patch delivery. We transfer app_module.{README,beam} to the OTP home directory and from the console run code:load_file(app_module).

Do you have any existing tools or workflows that rely on the CWD being in (or at the front of) the code path?

Sometimes when I am debugging an app, I put a .beam on CWD, but I can easily add -pa . before. So I am in favor of moving it to the last position for the reasons above.

Regarding removing it fully, I’d assume there are many teaching material that say erlc hello.erl and then ask folks to type hello:world()? Or even c(hello) in the shell, which as far as I know does not load it and therefore relies on . being there?

2 Likes

So adding “.” at the end of pathes could be automatically done on first (real and local) use in console, not at shell module start. Starting VM without shell would avoid this.

This is bad. It reminds me of the old ‘test’ trap for UNIX beginners. The problem it creates is that I can have a foo.beam in my current directory abd when I loD “it” i get sone other foo that I never heard of.

The fundamental problem is that a search path is simply the wrong tool for almost any job. It’s a very fragile approach. Make this change and user code will likely break. Dob’t mKe it and system code may break. Where the system looks for my modules and where it looks for its own should bot be mingled.

1 Like

As two modules cannot have the same name in Erlang, I would be grateful to know there is another one with same name in the future pathes my VM will use.

As there is no will to handle namespaces in Erlang, this is the cost to pay.

  1. It doesn’t fix the security risk.
    The fundamental bug is having all applications (including system ones)
    use the same search path. Fix that bug, and you won’t care where
    or whether “.” is in the user’s search path.
    Changes that don’t actually fix the problem they are devised for
    should be avoided.
  2. It still doesn’t fix the security risk.
    Changes that don’t actually fix the problem they are devised for
    should be avoided.
  3. Tools, no. Workflow, yes. If I have to create a scriptth
    ~/local/bin/erl => exec real-erl -pa . …
    then I can do so. But I’ll scream at the walls “Why didn’t they fix
    the actual problem?”

Lawrie Brown do work on Safe Erlang back in the 1990s.
It’s a real shame that none of his ideas seem to have been picked up and
put into production use.
I’m not sure if the insight that “having a single search path is wrong” was
explicit in his work on not, but that’s where I got it from (with a bit of

Eiffel LACE and OASIS catalogs).

Two modules with the same name was indeed one of the things allowed by Lawrie Brown’s Safe Erlang.
Safely allowed.
As for future paths, nobody can know it. There are more than a thousand modules in OTP.
If you want to know whether a module name you want to use is already taken,
code:which(proposed_module_name)
does the job.

But separating system path and user paths won’t.

I am not advocating separating system an user paths. I am saying that paths are just the wrong kind of tool for the job and you cannot patch a path-based approach into safety. We’ve known this for years in the case of $PATH and $LD_LIBRARY_PATH.

1 Like
  • Seems reasonable.
  • It would be inconvenient, particularly for teaching Erlang. But it’s not the end of the world.
  • Yes. Not actually tools, but slides and recordings of introductory lessons to the language.

Similar issue exists for header resolution - this should be consistent and either both contain CWD, or both don’t. The arguments in both cases are pretty similar, I see no reason to have a discrepancy in how these behave.

Just to brainstorm solutions: as an alternative to moving to -pz or completely removing CWD, it could be possible to keep it in a sort-of “log” mode - when traversing the path, the code server wouldn’t actually load the module, but log that a file in CWD was found and you could run with -pa or code:add_patha to make it load. If the primary use-case for CWD in load path is convenience or local dev workflows, this could be some sort of compromise to avoid some of the confusion from removing CWD completely (or moving it to the end).

I somewhat agree with the sentiment that load paths are problematic in general, but perhaps for a slightly different, but practical reason – it just ends up very slow. If you have a project with several hundred applications and several thousand modules, the fact that code loading is effectively O(N*M) shows up quite significantly. We have path caching, but it’s just a work-around of the problem. It’s very similar with the compiler and include resolution – in some cases compilation time can end up dominated by resolving includes, not actually compiling or optimising the code.

1 Like

This is a good point, but a module loaded/compiled with c(some_module_in_cwd) might rely on some_other_module_in_cwd (which could’ve been c(…)'d in a previous session) which will stop working if we remove CWD from the code path altogether. :frowning: