Skip to content

Commit 4f9ccdd

Browse files
bas-vkfjl
authored andcommitted
build: safe update of PATH on Windows (ethereum#3419)
NSIS has a default MAX_STR_LEN of 1024. If $ENV{PATH} is longer the returned string is truncated to an empty string. Its then not possible to distinguis between the variable not set or too long. As a result the variable is set with the location where geth and/or dev tools are installed. This may override any previous set values.
1 parent 4e36b1e commit 4f9ccdd

File tree

5 files changed

+164
-3
lines changed

5 files changed

+164
-3
lines changed

build/ci.go

+1
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ func doWindowsInstaller(cmdline []string) {
638638
build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil)
639639
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
640640
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
641+
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
641642
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
642643
build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755)
643644
build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755)

build/nsis.geth.nsi

+5
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
#
1818
# Requirements:
1919
# - NSIS, http://nsis.sourceforge.net/Main_Page
20+
# - NSIS Large Strings build, http://nsis.sourceforge.net/Special_Builds
2021
# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi)
2122
#
23+
# After intalling NSIS extra the NSIS Large Strings build zip and replace the makensis.exe and the
24+
# files found in Stub.
25+
#
2226
# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller
2327
#
2428
# TODO:
@@ -37,6 +41,7 @@ RequestExecutionLevel admin
3741
SetCompressor /SOLID lzma
3842

3943
!include LogicLib.nsh
44+
!include PathUpdate.nsh
4045
!include EnvVarUpdate.nsh
4146

4247
!macro VerifyUserIsAdmin

build/nsis.install.nsh

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ Section "Geth" GETH_IDX
3737
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
3838
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "A" "HKLM" "\\.\pipe\geth.ipc"
3939

40-
# Add geth to PATH
41-
${EnvVarUpdate} $0 "PATH" "A" "HKLM" $INSTDIR
40+
# Add instdir to PATH
41+
Push "$INSTDIR"
42+
Call AddToPath
4243
SectionEnd
4344

4445
# Install optional develop tools.

build/nsis.pathupdate.nsh

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
!include "WinMessages.nsh"
2+
3+
; see https://support.microsoft.com/en-us/kb/104011
4+
!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
5+
; HKEY_LOCAL_MACHINE = 0x80000002
6+
7+
; AddToPath - Appends dir to PATH
8+
; (does not work on Win9x/ME)
9+
;
10+
; Usage:
11+
; Push "dir"
12+
; Call AddToPath
13+
Function AddToPath
14+
Exch $0
15+
Push $1
16+
Push $2
17+
Push $3
18+
Push $4
19+
20+
; NSIS ReadRegStr returns empty string on string overflow
21+
; Native calls are used here to check actual length of PATH
22+
; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
23+
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
24+
IntCmp $4 0 0 done done
25+
26+
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
27+
; RegCloseKey($3)
28+
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
29+
System::Call "advapi32::RegCloseKey(i $3)"
30+
31+
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
32+
DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
33+
MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
34+
Goto done
35+
36+
IntCmp $4 0 +5 ; $4 != NO_ERROR
37+
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
38+
DetailPrint "AddToPath: unexpected error code $4"
39+
Goto done
40+
StrCpy $1 ""
41+
42+
; Check if already in PATH
43+
Push "$1;"
44+
Push "$0;"
45+
Call StrStr
46+
Pop $2
47+
StrCmp $2 "" 0 done
48+
Push "$1;"
49+
Push "$0\;"
50+
Call StrStr
51+
Pop $2
52+
StrCmp $2 "" 0 done
53+
54+
; Prevent NSIS string overflow
55+
StrLen $2 $0
56+
StrLen $3 $1
57+
IntOp $2 $2 + $3
58+
IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
59+
IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0
60+
DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
61+
MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}."
62+
Goto done
63+
64+
; Append dir to PATH
65+
DetailPrint "Add to PATH: $0"
66+
StrCpy $2 $1 1 -1
67+
StrCmp $2 ";" 0 +2
68+
StrCpy $1 $1 -1 ; remove trailing ';'
69+
StrCmp $1 "" +2 ; no leading ';'
70+
StrCpy $0 "$1;$0"
71+
72+
WriteRegExpandStr ${Environ} "PATH" $0
73+
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
74+
75+
done:
76+
Pop $4
77+
Pop $3
78+
Pop $2
79+
Pop $1
80+
Pop $0
81+
FunctionEnd
82+
83+
84+
; RemoveFromPath - Removes dir from PATH
85+
;
86+
; Usage:
87+
; Push "dir"
88+
; Call RemoveFromPath
89+
Function un.RemoveFromPath
90+
Exch $0
91+
Push $1
92+
Push $2
93+
Push $3
94+
Push $4
95+
Push $5
96+
Push $6
97+
98+
; NSIS ReadRegStr returns empty string on string overflow
99+
; Native calls are used here to check actual length of PATH
100+
; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
101+
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
102+
IntCmp $4 0 0 done done
103+
104+
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
105+
; RegCloseKey($3)
106+
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
107+
System::Call "advapi32::RegCloseKey(i $3)"
108+
109+
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
110+
DetailPrint "RemoveFromPath: original length $2 > ${NSIS_MAX_STRLEN}"
111+
MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
112+
Goto done
113+
114+
IntCmp $4 0 +5 ; $4 != NO_ERROR
115+
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
116+
DetailPrint "RemoveFromPath: unexpected error code $4"
117+
Goto done
118+
StrCpy $1 ""
119+
120+
; length < ${NSIS_MAX_STRLEN} -> ReadRegStr can be used
121+
ReadRegStr $1 ${Environ} "PATH"
122+
StrCpy $5 $1 1 -1
123+
StrCmp $5 ";" +2
124+
StrCpy $1 "$1;" ; ensure trailing ';'
125+
Push $1
126+
Push "$0;"
127+
Call un.StrStr
128+
Pop $2 ; pos of our dir
129+
StrCmp $2 "" done
130+
131+
DetailPrint "Remove from PATH: $0"
132+
StrLen $3 "$0;"
133+
StrLen $4 $2
134+
StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
135+
StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
136+
StrCpy $3 "$5$6"
137+
StrCpy $5 $3 1 -1
138+
StrCmp $5 ";" 0 +2
139+
StrCpy $3 $3 -1 ; remove trailing ';'
140+
WriteRegExpandStr ${Environ} "PATH" $3
141+
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
142+
143+
done:
144+
Pop $6
145+
Pop $5
146+
Pop $4
147+
Pop $3
148+
Pop $2
149+
Pop $1
150+
Pop $0
151+
FunctionEnd
152+
153+

build/nsis.uninstall.nsh

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ Section "Uninstall"
2525
${un.EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
2626

2727
# Remove install directory from PATH
28-
${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" $INSTDIR
28+
Push "$INSTDIR"
29+
Call un.RemoveFromPath
2930

3031
# Cleanup registry (deletes all sub keys)
3132
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}"

0 commit comments

Comments
 (0)