@@ -796,6 +796,74 @@ int Settings_write(const Settings* this, bool onCrash) {
796796 return r ;
797797}
798798
799+ static void Settings_resolveSymlink (char * * resolvedPath , const char * path ) {
800+ int fd = -1 ;
801+ int openFlags = O_NOCTTY | O_NOFOLLOW | O_NONBLOCK ;
802+ #ifdef O_EXEC
803+ openFlags |= O_EXEC ;
804+ #else
805+ // O_EXEC is not supported in Linux.
806+ openFlags |= O_RDONLY ;
807+ #endif
808+ #ifdef O_PATH
809+ // O_PATH is specific to Linux and FreeBSD.
810+ openFlags |= O_PATH ;
811+ #endif
812+ do {
813+ fd = open (path , openFlags );
814+ } while (fd < 0 && errno == EINTR );
815+
816+ if (fd < 0 )
817+ goto noPath ;
818+
819+ struct stat sb ;
820+ int err = fstat (fd , & sb );
821+ if (err )
822+ goto fileBroken ;
823+
824+ if (!S_ISLNK (sb .st_mode ) || (sb .st_uid != 0 && sb .st_uid != geteuid ())) {
825+ // Not a symbolic link or the symbolic link is not trusted.
826+ // Return the path to the link itself. This allows the link
827+ // target to be opened read-only when desirable.
828+ close (fd );
829+ * resolvedPath = xStrdup (path );
830+ return ;
831+ }
832+
833+ if (sb .st_size < 0 || sb .st_size >= PATH_MAX )
834+ goto fileBroken ;
835+
836+ char buf [PATH_MAX ];
837+ ssize_t len = readlinkat (fd , "" , buf , PATH_MAX );
838+ close (fd );
839+
840+ if (len < 0 || len > sb .st_size )
841+ goto noPath ;
842+
843+ buf [len ] = '\0' ;
844+ size_t dirnameLen = 0 ;
845+ if (buf [0 ] != '/' ) {
846+ const char * lastSlash = strrchr (path , '/' );
847+ dirnameLen = lastSlash ? (size_t )(lastSlash - (const char * )path ) : 0 ;
848+ }
849+ char * intermediatePath = xMalloc (dirnameLen + (size_t )len + 1 );
850+ memcpy (intermediatePath , path , dirnameLen );
851+ memcpy (intermediatePath + dirnameLen , buf , (size_t )len + 1 );
852+ char * ptr = realpath (intermediatePath , buf );
853+ free (intermediatePath );
854+ if (!ptr )
855+ goto noPath ;
856+
857+ * resolvedPath = xStrdup (buf );
858+ return ;
859+
860+ fileBroken :
861+ close (fd );
862+ noPath :
863+ * resolvedPath = xStrdup ("" );
864+ return ;
865+ }
866+
799867Settings * Settings_new (const Machine * host , Hashtable * dynamicMeters , Hashtable * dynamicColumns , Hashtable * dynamicScreens ) {
800868 Settings * this = xCalloc (1 , sizeof (Settings ));
801869
@@ -873,9 +941,7 @@ Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable*
873941 legacyDotfile = String_cat (home , "/.htoprc" );
874942 }
875943
876- this -> filename = xMalloc (PATH_MAX );
877- if (!realpath (this -> initialFilename , this -> filename ))
878- free_and_xStrdup (& this -> filename , this -> initialFilename );
944+ Settings_resolveSymlink (& this -> filename , this -> initialFilename );
879945
880946 this -> colorScheme = 0 ;
881947#ifdef HAVE_GETMOUSE
0 commit comments